@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/react.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo, forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
|
|
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
|
import { jsx } from 'react/jsx-runtime';
|
|
15
15
|
|
|
16
16
|
// src/react.tsx
|
|
@@ -267,6 +267,39 @@ function detectOoxmlType(buffer) {
|
|
|
267
267
|
}
|
|
268
268
|
return "application/zip";
|
|
269
269
|
}
|
|
270
|
+
function detectCfbfType(buffer) {
|
|
271
|
+
const bytes = new Uint8Array(buffer);
|
|
272
|
+
const hasUtf16le = (str) => {
|
|
273
|
+
const target = new Uint8Array(str.length * 2);
|
|
274
|
+
for (let i = 0; i < str.length; i++) {
|
|
275
|
+
target[i * 2] = str.charCodeAt(i);
|
|
276
|
+
target[i * 2 + 1] = 0;
|
|
277
|
+
}
|
|
278
|
+
const targetLen = target.length;
|
|
279
|
+
const max = bytes.length - targetLen;
|
|
280
|
+
for (let i = 0; i <= max; i++) {
|
|
281
|
+
let match = true;
|
|
282
|
+
for (let j = 0; j < targetLen; j++) {
|
|
283
|
+
if (bytes[i + j] !== target[j]) {
|
|
284
|
+
match = false;
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (match) return true;
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
};
|
|
292
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
293
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
294
|
+
}
|
|
295
|
+
if (hasUtf16le("WordDocument")) {
|
|
296
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
297
|
+
}
|
|
298
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
299
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
270
303
|
function extractExtension(nameOrUrl) {
|
|
271
304
|
try {
|
|
272
305
|
const url = new URL(nameOrUrl);
|
|
@@ -331,6 +364,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
331
364
|
} else {
|
|
332
365
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
333
366
|
}
|
|
367
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
368
|
+
if (metadata.extension) {
|
|
369
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
370
|
+
} else {
|
|
371
|
+
const cfbf = detectCfbfType(buffer);
|
|
372
|
+
if (cfbf) {
|
|
373
|
+
metadata.mimeType = cfbf.mime;
|
|
374
|
+
metadata.extension = cfbf.extension;
|
|
375
|
+
} else {
|
|
376
|
+
metadata.mimeType = magicMime;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
334
379
|
} else {
|
|
335
380
|
metadata.mimeType = magicMime;
|
|
336
381
|
}
|
|
@@ -341,7 +386,7 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
341
386
|
return { buffer, metadata };
|
|
342
387
|
}
|
|
343
388
|
function sanitizeSVG(svg) {
|
|
344
|
-
return
|
|
389
|
+
return DOMPurify2.sanitize(svg, {
|
|
345
390
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
346
391
|
});
|
|
347
392
|
}
|
|
@@ -401,6 +446,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
401
446
|
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>`;
|
|
402
447
|
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>`;
|
|
403
448
|
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>`;
|
|
449
|
+
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>`;
|
|
450
|
+
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>`;
|
|
451
|
+
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>`;
|
|
404
452
|
var ICON_MAP = {
|
|
405
453
|
"zoom-in": ICON_ZOOM_IN,
|
|
406
454
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -421,7 +469,12 @@ var ICON_MAP = {
|
|
|
421
469
|
"page-prev": ICON_PAGE_PREV,
|
|
422
470
|
"page-next": ICON_PAGE_NEXT,
|
|
423
471
|
"chevron-left": ICON_PAGE_PREV,
|
|
424
|
-
"chevron-right": ICON_PAGE_NEXT
|
|
472
|
+
"chevron-right": ICON_PAGE_NEXT,
|
|
473
|
+
"fast-forward": ICON_FAST_FORWARD,
|
|
474
|
+
"forward-10": ICON_FAST_FORWARD,
|
|
475
|
+
"rewind": ICON_REWIND,
|
|
476
|
+
"replay-10": ICON_REWIND,
|
|
477
|
+
"speed": ICON_SPEED
|
|
425
478
|
};
|
|
426
479
|
var ToolbarController = class {
|
|
427
480
|
el;
|
|
@@ -1389,6 +1442,16 @@ var MediaPlugin = class {
|
|
|
1389
1442
|
}
|
|
1390
1443
|
if (instance.play) {
|
|
1391
1444
|
actions.push(
|
|
1445
|
+
{
|
|
1446
|
+
id: "replay-10",
|
|
1447
|
+
icon: "replay-10",
|
|
1448
|
+
label: "Rewind 10s",
|
|
1449
|
+
type: "button",
|
|
1450
|
+
group: "actions",
|
|
1451
|
+
execute: () => {
|
|
1452
|
+
instance.rewind?.(10);
|
|
1453
|
+
}
|
|
1454
|
+
},
|
|
1392
1455
|
{
|
|
1393
1456
|
id: "play",
|
|
1394
1457
|
icon: "play",
|
|
@@ -1408,6 +1471,26 @@ var MediaPlugin = class {
|
|
|
1408
1471
|
execute: () => {
|
|
1409
1472
|
instance.pause?.();
|
|
1410
1473
|
}
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
id: "forward-10",
|
|
1477
|
+
icon: "forward-10",
|
|
1478
|
+
label: "Fast Forward 10s",
|
|
1479
|
+
type: "button",
|
|
1480
|
+
group: "actions",
|
|
1481
|
+
execute: () => {
|
|
1482
|
+
instance.fastForward?.(10);
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
id: "speed",
|
|
1487
|
+
icon: "speed",
|
|
1488
|
+
label: "Playback Speed",
|
|
1489
|
+
type: "button",
|
|
1490
|
+
group: "actions",
|
|
1491
|
+
execute: () => {
|
|
1492
|
+
instance.cycleSpeed?.();
|
|
1493
|
+
}
|
|
1411
1494
|
}
|
|
1412
1495
|
);
|
|
1413
1496
|
}
|
|
@@ -1530,6 +1613,28 @@ var MediaPlugin = class {
|
|
|
1530
1613
|
play: mediaElement ? () => mediaElement?.play() : void 0,
|
|
1531
1614
|
pause: mediaElement ? () => mediaElement?.pause() : void 0,
|
|
1532
1615
|
isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
|
|
1616
|
+
fastForward: mediaElement ? (seconds = 10) => {
|
|
1617
|
+
if (mediaElement) {
|
|
1618
|
+
mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
|
|
1619
|
+
}
|
|
1620
|
+
} : void 0,
|
|
1621
|
+
rewind: mediaElement ? (seconds = 10) => {
|
|
1622
|
+
if (mediaElement) {
|
|
1623
|
+
mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
|
|
1624
|
+
}
|
|
1625
|
+
} : void 0,
|
|
1626
|
+
cycleSpeed: mediaElement ? () => {
|
|
1627
|
+
if (mediaElement) {
|
|
1628
|
+
const speeds = [1, 1.25, 1.5, 2, 0.5];
|
|
1629
|
+
const curIndex = speeds.indexOf(mediaElement.playbackRate);
|
|
1630
|
+
const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
|
|
1631
|
+
mediaElement.playbackRate = speeds[nextIndex];
|
|
1632
|
+
}
|
|
1633
|
+
} : void 0,
|
|
1634
|
+
setPlaybackRate: mediaElement ? (rate) => {
|
|
1635
|
+
if (mediaElement) mediaElement.playbackRate = rate;
|
|
1636
|
+
} : void 0,
|
|
1637
|
+
getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
|
|
1533
1638
|
download: () => {
|
|
1534
1639
|
const a = document.createElement("a");
|
|
1535
1640
|
a.href = url;
|
|
@@ -1559,7 +1664,10 @@ var DocxPlugin = class {
|
|
|
1559
1664
|
supports(file) {
|
|
1560
1665
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1561
1666
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1562
|
-
|
|
1667
|
+
if (ext) {
|
|
1668
|
+
return this.extensions.includes(ext);
|
|
1669
|
+
}
|
|
1670
|
+
return this.mimeTypes.includes(mime || "");
|
|
1563
1671
|
}
|
|
1564
1672
|
getToolbarActions(instance) {
|
|
1565
1673
|
return [
|
|
@@ -1611,16 +1719,35 @@ var DocxPlugin = class {
|
|
|
1611
1719
|
wrapper.style.transformOrigin = "top center";
|
|
1612
1720
|
wrapper.style.transition = "transform 0.2s ease";
|
|
1613
1721
|
wrapper.style.padding = "24px 16px";
|
|
1614
|
-
wrapper.style.maxWidth = "
|
|
1722
|
+
wrapper.style.maxWidth = "950px";
|
|
1615
1723
|
wrapper.style.margin = "0 auto";
|
|
1616
1724
|
wrapper.style.boxSizing = "border-box";
|
|
1617
1725
|
ctx.container.style.overflow = "auto";
|
|
1618
1726
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
1619
1727
|
ctx.container.appendChild(wrapper);
|
|
1728
|
+
const styleOverride = document.createElement("style");
|
|
1729
|
+
styleOverride.textContent = `
|
|
1730
|
+
.fp-docx-wrapper .docx-wrapper {
|
|
1731
|
+
background: transparent !important;
|
|
1732
|
+
padding: 0 !important;
|
|
1733
|
+
display: flex !important;
|
|
1734
|
+
flex-direction: column !important;
|
|
1735
|
+
align-items: center !important;
|
|
1736
|
+
box-sizing: border-box !important;
|
|
1737
|
+
}
|
|
1738
|
+
.fp-docx-wrapper section.docx {
|
|
1739
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
|
|
1740
|
+
border-radius: 4px !important;
|
|
1741
|
+
margin-bottom: 24px !important;
|
|
1742
|
+
background-color: #ffffff !important;
|
|
1743
|
+
}
|
|
1744
|
+
`;
|
|
1745
|
+
ctx.container.appendChild(styleOverride);
|
|
1620
1746
|
let scale = 1;
|
|
1621
1747
|
let renderedSuccessfully = false;
|
|
1748
|
+
const createdBlobUrls = [];
|
|
1622
1749
|
try {
|
|
1623
|
-
await docx.renderAsync(ctx.buffer, wrapper,
|
|
1750
|
+
await docx.renderAsync(ctx.buffer, wrapper, wrapper, {
|
|
1624
1751
|
inWrapper: true,
|
|
1625
1752
|
ignoreWidth: false,
|
|
1626
1753
|
ignoreHeight: false,
|
|
@@ -1629,19 +1756,25 @@ var DocxPlugin = class {
|
|
|
1629
1756
|
breakPages: true,
|
|
1630
1757
|
experimental: true
|
|
1631
1758
|
});
|
|
1759
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1760
|
+
ctx.container.appendChild(wrapper);
|
|
1761
|
+
}
|
|
1632
1762
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
1633
1763
|
renderedSuccessfully = true;
|
|
1634
1764
|
}
|
|
1635
1765
|
} catch (err) {
|
|
1636
|
-
console.warn("[DocxPlugin] docx-preview failed, triggering native
|
|
1766
|
+
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
1637
1767
|
}
|
|
1638
1768
|
if (!renderedSuccessfully) {
|
|
1639
1769
|
try {
|
|
1640
1770
|
wrapper.innerHTML = "";
|
|
1641
|
-
this.renderXmlFallback(ctx, wrapper);
|
|
1771
|
+
this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
|
|
1772
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1773
|
+
ctx.container.appendChild(wrapper);
|
|
1774
|
+
}
|
|
1642
1775
|
renderedSuccessfully = true;
|
|
1643
1776
|
} catch (fallbackErr) {
|
|
1644
|
-
console.error("[DocxPlugin]
|
|
1777
|
+
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
1645
1778
|
wrapper.innerHTML = `
|
|
1646
1779
|
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
1647
1780
|
<div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
|
|
@@ -1649,10 +1782,17 @@ var DocxPlugin = class {
|
|
|
1649
1782
|
<p style="color: #64748b; margin: 0;">Could not parse document content</p>
|
|
1650
1783
|
</div>
|
|
1651
1784
|
`;
|
|
1785
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1786
|
+
ctx.container.appendChild(wrapper);
|
|
1787
|
+
}
|
|
1652
1788
|
}
|
|
1653
1789
|
}
|
|
1654
1790
|
const cleanup = () => {
|
|
1791
|
+
for (const url of createdBlobUrls) {
|
|
1792
|
+
URL.revokeObjectURL(url);
|
|
1793
|
+
}
|
|
1655
1794
|
wrapper.remove();
|
|
1795
|
+
styleOverride.remove();
|
|
1656
1796
|
ctx.container.innerHTML = "";
|
|
1657
1797
|
};
|
|
1658
1798
|
ctx.signal.addEventListener("abort", cleanup);
|
|
@@ -1689,13 +1829,50 @@ var DocxPlugin = class {
|
|
|
1689
1829
|
}
|
|
1690
1830
|
};
|
|
1691
1831
|
}
|
|
1692
|
-
renderXmlFallback(ctx, wrapper) {
|
|
1832
|
+
renderXmlFallback(ctx, wrapper, createdBlobUrls) {
|
|
1833
|
+
const magic = new Uint8Array(ctx.buffer.slice(0, 8));
|
|
1834
|
+
if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
|
|
1835
|
+
this.renderBinaryDocFallback(ctx, wrapper);
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1693
1838
|
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
1694
|
-
const
|
|
1839
|
+
const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
|
|
1840
|
+
const docXmlEntry = docKey ? unzipped[docKey] : void 0;
|
|
1695
1841
|
if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
|
|
1696
1842
|
const xmlStr = strFromU8(docXmlEntry);
|
|
1697
1843
|
const parser = new DOMParser();
|
|
1698
1844
|
const doc = parser.parseFromString(xmlStr, "application/xml");
|
|
1845
|
+
const imageMap = {};
|
|
1846
|
+
const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
|
|
1847
|
+
if (relsKey && unzipped[relsKey]) {
|
|
1848
|
+
try {
|
|
1849
|
+
const relsXml = strFromU8(unzipped[relsKey]);
|
|
1850
|
+
const relsDoc = parser.parseFromString(relsXml, "application/xml");
|
|
1851
|
+
const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
|
|
1852
|
+
for (const rel of relEls) {
|
|
1853
|
+
const rId = rel.getAttribute("Id");
|
|
1854
|
+
const target = rel.getAttribute("Target");
|
|
1855
|
+
if (rId && target) {
|
|
1856
|
+
const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
|
|
1857
|
+
const fullTargetKey = Object.keys(unzipped).find((k) => {
|
|
1858
|
+
const norm = k.replace(/^[./\\]+/, "").toLowerCase();
|
|
1859
|
+
return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
|
|
1860
|
+
});
|
|
1861
|
+
if (fullTargetKey && unzipped[fullTargetKey]) {
|
|
1862
|
+
const bytes = unzipped[fullTargetKey];
|
|
1863
|
+
const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
|
|
1864
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
|
|
1865
|
+
const blob = new Blob([bytes], { type: mime });
|
|
1866
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1867
|
+
imageMap[rId] = blobUrl;
|
|
1868
|
+
createdBlobUrls.push(blobUrl);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
} catch (relsErr) {
|
|
1873
|
+
console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1699
1876
|
const card = document.createElement("div");
|
|
1700
1877
|
card.className = "fp-docx-page-card";
|
|
1701
1878
|
card.style.backgroundColor = "#ffffff";
|
|
@@ -1713,7 +1890,7 @@ var DocxPlugin = class {
|
|
|
1713
1890
|
const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
|
|
1714
1891
|
const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
|
|
1715
1892
|
const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
|
|
1716
|
-
const textContent = this.extractParagraphHtml(child);
|
|
1893
|
+
const textContent = this.extractParagraphHtml(child, imageMap);
|
|
1717
1894
|
if (!textContent.trim()) {
|
|
1718
1895
|
html += '<div style="height: 10px;"></div>';
|
|
1719
1896
|
continue;
|
|
@@ -1739,7 +1916,7 @@ var DocxPlugin = class {
|
|
|
1739
1916
|
html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
1740
1917
|
const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
|
|
1741
1918
|
cells.forEach((tc) => {
|
|
1742
|
-
const cellText = this.extractParagraphHtml(tc);
|
|
1919
|
+
const cellText = this.extractParagraphHtml(tc, imageMap);
|
|
1743
1920
|
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || " "}</td>`;
|
|
1744
1921
|
});
|
|
1745
1922
|
html += "</tr>";
|
|
@@ -1747,34 +1924,63 @@ var DocxPlugin = class {
|
|
|
1747
1924
|
html += "</table>";
|
|
1748
1925
|
}
|
|
1749
1926
|
}
|
|
1750
|
-
card.innerHTML =
|
|
1751
|
-
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
|
|
1927
|
+
card.innerHTML = DOMPurify2.sanitize(html, {
|
|
1928
|
+
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
|
|
1752
1929
|
ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
|
|
1753
1930
|
});
|
|
1754
1931
|
wrapper.appendChild(card);
|
|
1755
1932
|
}
|
|
1756
|
-
extractParagraphHtml(pElement) {
|
|
1933
|
+
extractParagraphHtml(pElement, imageMap = {}) {
|
|
1757
1934
|
let result = "";
|
|
1935
|
+
const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
|
|
1936
|
+
for (const drawing of drawings) {
|
|
1937
|
+
const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
|
|
1938
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1939
|
+
if (rId && imageMap[rId]) {
|
|
1940
|
+
result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1758
1943
|
const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
|
|
1759
|
-
if (runs.length === 0) {
|
|
1760
|
-
return
|
|
1944
|
+
if (runs.length === 0 && !result) {
|
|
1945
|
+
return DOMPurify2.sanitize(pElement.textContent || "");
|
|
1761
1946
|
}
|
|
1762
1947
|
for (const r of runs) {
|
|
1948
|
+
const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
|
|
1949
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1950
|
+
if (rId && imageMap[rId]) {
|
|
1951
|
+
result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
|
|
1952
|
+
}
|
|
1953
|
+
const brs = r.getElementsByTagNameNS("*", "br");
|
|
1954
|
+
for (let i = 0; i < brs.length; i++) {
|
|
1955
|
+
result += "<br/>";
|
|
1956
|
+
}
|
|
1957
|
+
const tabs = r.getElementsByTagNameNS("*", "tab");
|
|
1958
|
+
if (tabs.length > 0) {
|
|
1959
|
+
result += " ";
|
|
1960
|
+
}
|
|
1763
1961
|
const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
|
|
1764
1962
|
const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
|
|
1765
1963
|
const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
|
|
1766
1964
|
const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
|
|
1965
|
+
const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
|
|
1767
1966
|
const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
|
|
1768
1967
|
const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
|
|
1968
|
+
const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
|
|
1969
|
+
const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
|
|
1769
1970
|
const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
|
|
1770
1971
|
let text = texts.map((t) => t.textContent || "").join("");
|
|
1771
1972
|
if (!text) continue;
|
|
1772
|
-
text =
|
|
1973
|
+
text = DOMPurify2.sanitize(text);
|
|
1773
1974
|
let styles = "";
|
|
1774
1975
|
if (isBold) styles += "font-weight: bold; ";
|
|
1775
1976
|
if (isItalic) styles += "font-style: italic; ";
|
|
1776
1977
|
if (isUnderline) styles += "text-decoration: underline; ";
|
|
1978
|
+
if (isStrike) styles += "text-decoration: line-through; ";
|
|
1777
1979
|
if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
|
|
1980
|
+
if (szVal) {
|
|
1981
|
+
const pt = parseInt(szVal, 10) / 2;
|
|
1982
|
+
if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
|
|
1983
|
+
}
|
|
1778
1984
|
if (styles) {
|
|
1779
1985
|
result += `<span style="${styles}">${text}</span>`;
|
|
1780
1986
|
} else {
|
|
@@ -1783,6 +1989,52 @@ var DocxPlugin = class {
|
|
|
1783
1989
|
}
|
|
1784
1990
|
return result;
|
|
1785
1991
|
}
|
|
1992
|
+
renderBinaryDocFallback(ctx, wrapper) {
|
|
1993
|
+
const card = document.createElement("div");
|
|
1994
|
+
card.className = "fp-docx-page-card";
|
|
1995
|
+
card.style.backgroundColor = "#ffffff";
|
|
1996
|
+
card.style.borderRadius = "6px";
|
|
1997
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
1998
|
+
card.style.padding = "48px 56px";
|
|
1999
|
+
card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
2000
|
+
card.style.color = "#1e293b";
|
|
2001
|
+
card.style.lineHeight = "1.6";
|
|
2002
|
+
try {
|
|
2003
|
+
const cfbf = new CfbfReader(ctx.buffer);
|
|
2004
|
+
const wordDocStream = cfbf.readStream("WordDocument");
|
|
2005
|
+
if (wordDocStream && wordDocStream.length >= 512) {
|
|
2006
|
+
const text2 = this.extractReadableStrings(wordDocStream);
|
|
2007
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
|
|
2008
|
+
wrapper.appendChild(card);
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
} catch {
|
|
2012
|
+
}
|
|
2013
|
+
const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
|
|
2014
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
|
|
2015
|
+
wrapper.appendChild(card);
|
|
2016
|
+
}
|
|
2017
|
+
extractReadableStrings(bytes) {
|
|
2018
|
+
let result = "";
|
|
2019
|
+
let current = "";
|
|
2020
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2021
|
+
const b = bytes[i];
|
|
2022
|
+
if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
|
|
2023
|
+
current += String.fromCharCode(b);
|
|
2024
|
+
} else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
|
|
2025
|
+
continue;
|
|
2026
|
+
} else {
|
|
2027
|
+
if (current.trim().length > 3) {
|
|
2028
|
+
result += current.trim() + "\n\n";
|
|
2029
|
+
}
|
|
2030
|
+
current = "";
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (current.trim().length > 3) {
|
|
2034
|
+
result += current.trim();
|
|
2035
|
+
}
|
|
2036
|
+
return result;
|
|
2037
|
+
}
|
|
1786
2038
|
};
|
|
1787
2039
|
function docxPlugin() {
|
|
1788
2040
|
return new DocxPlugin();
|
|
@@ -1804,7 +2056,10 @@ var ExcelPlugin = class {
|
|
|
1804
2056
|
supports(file) {
|
|
1805
2057
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1806
2058
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1807
|
-
|
|
2059
|
+
if (ext) {
|
|
2060
|
+
return this.extensions.includes(ext);
|
|
2061
|
+
}
|
|
2062
|
+
return this.mimeTypes.includes(mime || "");
|
|
1808
2063
|
}
|
|
1809
2064
|
getToolbarActions(instance) {
|
|
1810
2065
|
return [
|
|
@@ -2594,7 +2849,7 @@ var MarkdownPlugin = class {
|
|
|
2594
2849
|
gfm: true,
|
|
2595
2850
|
breaks: true
|
|
2596
2851
|
});
|
|
2597
|
-
const cleanHtml =
|
|
2852
|
+
const cleanHtml = DOMPurify2.sanitize(rawHtml, {
|
|
2598
2853
|
USE_PROFILES: { html: true }
|
|
2599
2854
|
});
|
|
2600
2855
|
const wrapper = document.createElement("div");
|
|
@@ -2746,7 +3001,10 @@ var PptxPlugin = class {
|
|
|
2746
3001
|
supports(file) {
|
|
2747
3002
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2748
3003
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2749
|
-
|
|
3004
|
+
if (ext) {
|
|
3005
|
+
return this.extensions.includes(ext);
|
|
3006
|
+
}
|
|
3007
|
+
return this.mimeTypes.includes(mime || "");
|
|
2750
3008
|
}
|
|
2751
3009
|
getToolbarActions(instance) {
|
|
2752
3010
|
return [
|
|
@@ -3191,6 +3449,9 @@ var RtfPlugin = class {
|
|
|
3191
3449
|
ctx.container.appendChild(wrapper);
|
|
3192
3450
|
let scale = 1;
|
|
3193
3451
|
try {
|
|
3452
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3453
|
+
RTFJS.loggingEnabled(false);
|
|
3454
|
+
}
|
|
3194
3455
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3195
3456
|
const htmlElements = await doc.render();
|
|
3196
3457
|
for (const el of htmlElements) {
|
|
@@ -3309,7 +3570,7 @@ var HtmlPreviewPlugin = class {
|
|
|
3309
3570
|
}
|
|
3310
3571
|
async render(ctx) {
|
|
3311
3572
|
const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
|
|
3312
|
-
const sanitized =
|
|
3573
|
+
const sanitized = DOMPurify2.sanitize(rawHtml, {
|
|
3313
3574
|
WHOLE_DOCUMENT: true,
|
|
3314
3575
|
ADD_TAGS: ["style", "link"],
|
|
3315
3576
|
ADD_ATTR: ["target", "rel"]
|
|
@@ -3389,7 +3650,10 @@ var OpenDocumentPlugin = class {
|
|
|
3389
3650
|
supports(file) {
|
|
3390
3651
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3391
3652
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3392
|
-
|
|
3653
|
+
if (ext) {
|
|
3654
|
+
return this.extensions.includes(ext);
|
|
3655
|
+
}
|
|
3656
|
+
return this.mimeTypes.includes(mime || "");
|
|
3393
3657
|
}
|
|
3394
3658
|
getToolbarActions(instance) {
|
|
3395
3659
|
const isPresentation = instance.isPresentation;
|
|
@@ -3552,7 +3816,7 @@ var OpenDocumentPlugin = class {
|
|
|
3552
3816
|
wrapper.style.padding = "48px";
|
|
3553
3817
|
wrapper.style.minHeight = "100%";
|
|
3554
3818
|
const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
|
|
3555
|
-
wrapper.innerHTML =
|
|
3819
|
+
wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
|
|
3556
3820
|
ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
|
|
3557
3821
|
ADD_ATTR: ["style", "colspan", "rowspan"]
|
|
3558
3822
|
});
|
|
@@ -3804,7 +4068,10 @@ var DocPlugin = class {
|
|
|
3804
4068
|
supports(file) {
|
|
3805
4069
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3806
4070
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3807
|
-
|
|
4071
|
+
if (ext) {
|
|
4072
|
+
return this.extensions.includes(ext);
|
|
4073
|
+
}
|
|
4074
|
+
return this.mimeTypes.includes(mime || "");
|
|
3808
4075
|
}
|
|
3809
4076
|
getToolbarActions(instance) {
|
|
3810
4077
|
return [
|
|
@@ -4027,22 +4294,22 @@ var DocPlugin = class {
|
|
|
4027
4294
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4028
4295
|
*/
|
|
4029
4296
|
extractStringsFromBytes(bytes) {
|
|
4030
|
-
const
|
|
4031
|
-
const
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4042
|
-
|
|
4297
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4298
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4299
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4300
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4301
|
+
const candidateLines = [];
|
|
4302
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4303
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4304
|
+
const trimmed = run.trim();
|
|
4305
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4306
|
+
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)) {
|
|
4307
|
+
seen.add(trimmed);
|
|
4308
|
+
candidateLines.push(trimmed);
|
|
4309
|
+
}
|
|
4043
4310
|
}
|
|
4044
4311
|
}
|
|
4045
|
-
return
|
|
4312
|
+
return candidateLines.join("\n\n");
|
|
4046
4313
|
}
|
|
4047
4314
|
heuristicTextExtraction(buffer) {
|
|
4048
4315
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4072,24 +4339,24 @@ var DocPlugin = class {
|
|
|
4072
4339
|
html += "</ul>";
|
|
4073
4340
|
inList = false;
|
|
4074
4341
|
}
|
|
4075
|
-
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${
|
|
4342
|
+
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>`;
|
|
4076
4343
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
4077
4344
|
if (!inList) {
|
|
4078
4345
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
4079
4346
|
inList = true;
|
|
4080
4347
|
}
|
|
4081
4348
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
4082
|
-
html += `<li style="margin: 4px 0; line-height: 1.6;">${
|
|
4349
|
+
html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
|
|
4083
4350
|
} else {
|
|
4084
4351
|
if (inList) {
|
|
4085
4352
|
html += "</ul>";
|
|
4086
4353
|
inList = false;
|
|
4087
4354
|
}
|
|
4088
|
-
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${
|
|
4355
|
+
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
|
|
4089
4356
|
}
|
|
4090
4357
|
}
|
|
4091
4358
|
if (inList) html += "</ul>";
|
|
4092
|
-
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${
|
|
4359
|
+
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
|
|
4093
4360
|
}
|
|
4094
4361
|
};
|
|
4095
4362
|
function docPlugin() {
|
|
@@ -4097,14 +4364,17 @@ function docPlugin() {
|
|
|
4097
4364
|
}
|
|
4098
4365
|
var PptPlugin = class {
|
|
4099
4366
|
id = "ppt";
|
|
4100
|
-
name = "
|
|
4367
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4101
4368
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4102
4369
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4103
|
-
weight =
|
|
4370
|
+
weight = 85;
|
|
4104
4371
|
supports(file) {
|
|
4105
4372
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4106
4373
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4107
|
-
|
|
4374
|
+
if (ext) {
|
|
4375
|
+
return this.extensions.includes(ext);
|
|
4376
|
+
}
|
|
4377
|
+
return this.mimeTypes.includes(mime || "");
|
|
4108
4378
|
}
|
|
4109
4379
|
getToolbarActions(instance) {
|
|
4110
4380
|
return [
|
|
@@ -4192,19 +4462,15 @@ var PptPlugin = class {
|
|
|
4192
4462
|
container.style.alignItems = "center";
|
|
4193
4463
|
container.style.padding = "32px 16px";
|
|
4194
4464
|
container.style.backgroundColor = "#0f172a";
|
|
4465
|
+
container.style.boxSizing = "border-box";
|
|
4195
4466
|
const slideCard = document.createElement("div");
|
|
4196
4467
|
slideCard.className = "fp-ppt-slide-card";
|
|
4197
4468
|
slideCard.style.width = "960px";
|
|
4198
|
-
slideCard.style.maxWidth = "
|
|
4469
|
+
slideCard.style.maxWidth = "92%";
|
|
4199
4470
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4200
4471
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4201
|
-
slideCard.style.boxShadow = "0
|
|
4472
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4202
4473
|
slideCard.style.borderRadius = "8px";
|
|
4203
|
-
slideCard.style.padding = "48px";
|
|
4204
|
-
slideCard.style.display = "flex";
|
|
4205
|
-
slideCard.style.flexDirection = "column";
|
|
4206
|
-
slideCard.style.justifyContent = "center";
|
|
4207
|
-
slideCard.style.alignItems = "center";
|
|
4208
4474
|
slideCard.style.boxSizing = "border-box";
|
|
4209
4475
|
slideCard.style.position = "relative";
|
|
4210
4476
|
slideCard.style.overflow = "hidden";
|
|
@@ -4215,21 +4481,25 @@ var PptPlugin = class {
|
|
|
4215
4481
|
let scale = 1;
|
|
4216
4482
|
let currentSlide = 1;
|
|
4217
4483
|
let slides = [];
|
|
4484
|
+
const createdBlobUrls = [];
|
|
4218
4485
|
try {
|
|
4219
4486
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4220
4487
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4221
4488
|
if (!pptStream || pptStream.length < 512) {
|
|
4222
4489
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4223
4490
|
}
|
|
4224
|
-
|
|
4491
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4492
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4225
4493
|
} catch (err) {
|
|
4226
4494
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4227
4495
|
}
|
|
4228
4496
|
if (slides.length === 0) {
|
|
4229
4497
|
slides = [
|
|
4230
4498
|
{
|
|
4499
|
+
slideIndex: 1,
|
|
4231
4500
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4232
|
-
|
|
4501
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4502
|
+
tableColumns: []
|
|
4233
4503
|
}
|
|
4234
4504
|
];
|
|
4235
4505
|
}
|
|
@@ -4238,23 +4508,73 @@ var PptPlugin = class {
|
|
|
4238
4508
|
currentSlide = idx;
|
|
4239
4509
|
const s = slides[idx - 1];
|
|
4240
4510
|
if (!s) return;
|
|
4511
|
+
let contentHtml = "";
|
|
4512
|
+
if (s.pictureUrl) {
|
|
4513
|
+
contentHtml = `
|
|
4514
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4515
|
+
<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;" />
|
|
4516
|
+
</div>
|
|
4517
|
+
`;
|
|
4518
|
+
} else if (s.tableColumns.length > 0) {
|
|
4519
|
+
const cols = s.tableColumns;
|
|
4520
|
+
contentHtml = `
|
|
4521
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4522
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4523
|
+
<thead>
|
|
4524
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4525
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
4526
|
+
</tr>
|
|
4527
|
+
</thead>
|
|
4528
|
+
<tbody>
|
|
4529
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4530
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4531
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4532
|
+
</tr>
|
|
4533
|
+
`).join("")}
|
|
4534
|
+
</tbody>
|
|
4535
|
+
</table>
|
|
4536
|
+
</div>
|
|
4537
|
+
`;
|
|
4538
|
+
} else {
|
|
4539
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4540
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4541
|
+
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("");
|
|
4542
|
+
}).join("");
|
|
4543
|
+
contentHtml = `
|
|
4544
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4545
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4546
|
+
</div>
|
|
4547
|
+
`;
|
|
4548
|
+
}
|
|
4241
4549
|
slideCard.innerHTML = `
|
|
4242
|
-
<div style="
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4249
|
-
|
|
4250
|
-
|
|
4550
|
+
<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;">
|
|
4551
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4552
|
+
<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;">
|
|
4553
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4554
|
+
${DOMPurify2.sanitize(s.title)}
|
|
4555
|
+
</h1>
|
|
4556
|
+
${s.subtitle ? `
|
|
4557
|
+
<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);">
|
|
4558
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
4559
|
+
</span>
|
|
4560
|
+
` : `
|
|
4561
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4562
|
+
Slide ${idx} / ${totalSlides}
|
|
4563
|
+
</span>
|
|
4564
|
+
`}
|
|
4251
4565
|
</div>
|
|
4566
|
+
|
|
4567
|
+
<!-- Slide Content -->
|
|
4568
|
+
${contentHtml}
|
|
4252
4569
|
</div>
|
|
4253
4570
|
`;
|
|
4254
4571
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4255
4572
|
};
|
|
4256
4573
|
renderSlide(1);
|
|
4257
4574
|
const cleanup = () => {
|
|
4575
|
+
for (const u of createdBlobUrls) {
|
|
4576
|
+
URL.revokeObjectURL(u);
|
|
4577
|
+
}
|
|
4258
4578
|
container.remove();
|
|
4259
4579
|
ctx.container.innerHTML = "";
|
|
4260
4580
|
};
|
|
@@ -4294,13 +4614,48 @@ var PptPlugin = class {
|
|
|
4294
4614
|
if (!ctx2d) return;
|
|
4295
4615
|
canvas.width = 160;
|
|
4296
4616
|
canvas.height = 90;
|
|
4297
|
-
ctx2d.fillStyle = "#
|
|
4617
|
+
ctx2d.fillStyle = "#334155";
|
|
4298
4618
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4299
|
-
ctx2d.fillStyle = "#
|
|
4300
|
-
ctx2d.
|
|
4301
|
-
ctx2d.
|
|
4302
|
-
|
|
4303
|
-
ctx2d.
|
|
4619
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4620
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4621
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4622
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4623
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4624
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4625
|
+
ctx2d.textAlign = "left";
|
|
4626
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4627
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4628
|
+
if (s.subtitle) {
|
|
4629
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4630
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4631
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4632
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4633
|
+
ctx2d.textAlign = "center";
|
|
4634
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4635
|
+
}
|
|
4636
|
+
if (s.pictureUrl) {
|
|
4637
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4638
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4639
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4640
|
+
ctx2d.font = "8px sans-serif";
|
|
4641
|
+
ctx2d.textAlign = "center";
|
|
4642
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4643
|
+
} else if (s.tableColumns.length > 0) {
|
|
4644
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4645
|
+
ctx2d.lineWidth = 1;
|
|
4646
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4647
|
+
for (let l = 1; l <= 3; l++) {
|
|
4648
|
+
ctx2d.beginPath();
|
|
4649
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4650
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4651
|
+
ctx2d.stroke();
|
|
4652
|
+
}
|
|
4653
|
+
} else {
|
|
4654
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4655
|
+
for (let l = 0; l < 4; l++) {
|
|
4656
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4657
|
+
}
|
|
4658
|
+
}
|
|
4304
4659
|
}
|
|
4305
4660
|
}));
|
|
4306
4661
|
},
|
|
@@ -4318,66 +4673,165 @@ var PptPlugin = class {
|
|
|
4318
4673
|
}
|
|
4319
4674
|
};
|
|
4320
4675
|
}
|
|
4676
|
+
/**
|
|
4677
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4678
|
+
*/
|
|
4679
|
+
extractPictures(cfbf, createdUrls) {
|
|
4680
|
+
const urls = [];
|
|
4681
|
+
try {
|
|
4682
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4683
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4684
|
+
const pBuf = new Uint8Array(picStream);
|
|
4685
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4686
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4687
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4688
|
+
let match = true;
|
|
4689
|
+
for (let j = 0; j < 8; j++) {
|
|
4690
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4691
|
+
match = false;
|
|
4692
|
+
break;
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
if (match) {
|
|
4696
|
+
let endIdx = -1;
|
|
4697
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4698
|
+
let endMatch = true;
|
|
4699
|
+
for (let j = 0; j < 8; j++) {
|
|
4700
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4701
|
+
endMatch = false;
|
|
4702
|
+
break;
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
if (endMatch) {
|
|
4706
|
+
endIdx = k + 8;
|
|
4707
|
+
break;
|
|
4708
|
+
}
|
|
4709
|
+
}
|
|
4710
|
+
if (endIdx !== -1) {
|
|
4711
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4712
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4713
|
+
const url = URL.createObjectURL(blob);
|
|
4714
|
+
urls.push(url);
|
|
4715
|
+
createdUrls.push(url);
|
|
4716
|
+
i = endIdx;
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4720
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4721
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4722
|
+
let endIdx = -1;
|
|
4723
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4724
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4725
|
+
endIdx = k + 2;
|
|
4726
|
+
break;
|
|
4727
|
+
}
|
|
4728
|
+
}
|
|
4729
|
+
if (endIdx !== -1) {
|
|
4730
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4731
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4732
|
+
const url = URL.createObjectURL(blob);
|
|
4733
|
+
urls.push(url);
|
|
4734
|
+
createdUrls.push(url);
|
|
4735
|
+
i = endIdx;
|
|
4736
|
+
}
|
|
4737
|
+
}
|
|
4738
|
+
}
|
|
4739
|
+
} catch (err) {
|
|
4740
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4741
|
+
}
|
|
4742
|
+
return urls;
|
|
4743
|
+
}
|
|
4321
4744
|
/**
|
|
4322
4745
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4323
4746
|
*/
|
|
4324
|
-
extractSlides(stream) {
|
|
4747
|
+
extractSlides(stream, pictures) {
|
|
4325
4748
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4326
4749
|
const len = stream.length;
|
|
4327
4750
|
let offset = 0;
|
|
4328
4751
|
const slides = [];
|
|
4329
|
-
let
|
|
4752
|
+
let picIdx = 0;
|
|
4330
4753
|
while (offset + 8 <= len) {
|
|
4331
4754
|
const recVerInst = view.getUint16(offset, true);
|
|
4332
4755
|
const recType = view.getUint16(offset + 2, true);
|
|
4333
4756
|
const recLen = view.getUint32(offset + 4, true);
|
|
4757
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4334
4758
|
if (recType === 1006) {
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4759
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4760
|
+
const rawTexts = [];
|
|
4761
|
+
let hasOle = false;
|
|
4762
|
+
let sOff = offset + 8;
|
|
4763
|
+
while (sOff + 8 <= slideEnd) {
|
|
4764
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4765
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4766
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4767
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4768
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4769
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4770
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4771
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4772
|
+
rawTexts.push(txt);
|
|
4773
|
+
}
|
|
4774
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4775
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4776
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4777
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4778
|
+
rawTexts.push(txt);
|
|
4779
|
+
}
|
|
4780
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4781
|
+
hasOle = true;
|
|
4782
|
+
}
|
|
4783
|
+
if (cIsContainer) sOff += 8;
|
|
4784
|
+
else sOff += 8 + cLen;
|
|
4340
4785
|
}
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4786
|
+
let title = "";
|
|
4787
|
+
let subtitle = "";
|
|
4788
|
+
const paragraphs = [];
|
|
4789
|
+
const tableColumns = [];
|
|
4790
|
+
for (const t of rawTexts) {
|
|
4791
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4792
|
+
title = t;
|
|
4793
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4794
|
+
tableColumns.push(t);
|
|
4795
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4796
|
+
subtitle = t;
|
|
4797
|
+
} else {
|
|
4798
|
+
paragraphs.push(t);
|
|
4799
|
+
}
|
|
4349
4800
|
}
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4354
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4355
|
-
currentSlideTexts.push(text);
|
|
4801
|
+
let pictureUrl = null;
|
|
4802
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4803
|
+
pictureUrl = pictures[picIdx++];
|
|
4356
4804
|
}
|
|
4805
|
+
slides.push({
|
|
4806
|
+
slideIndex: slides.length + 1,
|
|
4807
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4808
|
+
subtitle,
|
|
4809
|
+
paragraphs,
|
|
4810
|
+
tableColumns,
|
|
4811
|
+
pictureUrl,
|
|
4812
|
+
hasOle
|
|
4813
|
+
});
|
|
4357
4814
|
}
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
offset += 8;
|
|
4361
|
-
} else {
|
|
4362
|
-
offset += 8 + recLen;
|
|
4363
|
-
}
|
|
4364
|
-
}
|
|
4365
|
-
if (currentSlideTexts.length > 0) {
|
|
4366
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4367
|
-
const texts = currentSlideTexts.slice(1);
|
|
4368
|
-
slides.push({ title, texts });
|
|
4815
|
+
if (isContainer) offset += 8;
|
|
4816
|
+
else offset += 8 + recLen;
|
|
4369
4817
|
}
|
|
4370
4818
|
if (slides.length === 0) {
|
|
4371
4819
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4372
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4373
|
-
const
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4820
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4821
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4822
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4823
|
+
);
|
|
4824
|
+
if (clean.length > 0) {
|
|
4825
|
+
const chunkSize = 3;
|
|
4826
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4827
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4378
4828
|
slides.push({
|
|
4829
|
+
slideIndex: slides.length + 1,
|
|
4379
4830
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4380
|
-
|
|
4831
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4832
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4833
|
+
tableColumns: [],
|
|
4834
|
+
pictureUrl: pictures[slides.length] || null
|
|
4381
4835
|
});
|
|
4382
4836
|
}
|
|
4383
4837
|
}
|