@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.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var react = require('react');
|
|
6
|
-
var
|
|
6
|
+
var DOMPurify2 = require('dompurify');
|
|
7
7
|
var docx = require('docx-preview');
|
|
8
8
|
var fflate = require('fflate');
|
|
9
9
|
var XLSX = require('xlsx');
|
|
@@ -14,7 +14,7 @@ var THREE = require('three');
|
|
|
14
14
|
var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
|
|
15
15
|
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
16
16
|
var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
|
|
17
|
-
var
|
|
17
|
+
var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
|
|
18
18
|
var jsxRuntime = require('react/jsx-runtime');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -37,11 +37,12 @@ function _interopNamespace(e) {
|
|
|
37
37
|
return Object.freeze(n);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
var
|
|
40
|
+
var DOMPurify2__default = /*#__PURE__*/_interopDefault(DOMPurify2);
|
|
41
41
|
var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
|
|
42
42
|
var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
|
|
43
43
|
var hljs__default = /*#__PURE__*/_interopDefault(hljs);
|
|
44
44
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
45
|
+
var RTFJS__namespace = /*#__PURE__*/_interopNamespace(RTFJS);
|
|
45
46
|
|
|
46
47
|
// src/react.tsx
|
|
47
48
|
var EventEmitter = class {
|
|
@@ -297,6 +298,39 @@ function detectOoxmlType(buffer) {
|
|
|
297
298
|
}
|
|
298
299
|
return "application/zip";
|
|
299
300
|
}
|
|
301
|
+
function detectCfbfType(buffer) {
|
|
302
|
+
const bytes = new Uint8Array(buffer);
|
|
303
|
+
const hasUtf16le = (str) => {
|
|
304
|
+
const target = new Uint8Array(str.length * 2);
|
|
305
|
+
for (let i = 0; i < str.length; i++) {
|
|
306
|
+
target[i * 2] = str.charCodeAt(i);
|
|
307
|
+
target[i * 2 + 1] = 0;
|
|
308
|
+
}
|
|
309
|
+
const targetLen = target.length;
|
|
310
|
+
const max = bytes.length - targetLen;
|
|
311
|
+
for (let i = 0; i <= max; i++) {
|
|
312
|
+
let match = true;
|
|
313
|
+
for (let j = 0; j < targetLen; j++) {
|
|
314
|
+
if (bytes[i + j] !== target[j]) {
|
|
315
|
+
match = false;
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (match) return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
};
|
|
323
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
324
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
325
|
+
}
|
|
326
|
+
if (hasUtf16le("WordDocument")) {
|
|
327
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
328
|
+
}
|
|
329
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
330
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
300
334
|
function extractExtension(nameOrUrl) {
|
|
301
335
|
try {
|
|
302
336
|
const url = new URL(nameOrUrl);
|
|
@@ -361,6 +395,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
361
395
|
} else {
|
|
362
396
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
363
397
|
}
|
|
398
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
399
|
+
if (metadata.extension) {
|
|
400
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
401
|
+
} else {
|
|
402
|
+
const cfbf = detectCfbfType(buffer);
|
|
403
|
+
if (cfbf) {
|
|
404
|
+
metadata.mimeType = cfbf.mime;
|
|
405
|
+
metadata.extension = cfbf.extension;
|
|
406
|
+
} else {
|
|
407
|
+
metadata.mimeType = magicMime;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
364
410
|
} else {
|
|
365
411
|
metadata.mimeType = magicMime;
|
|
366
412
|
}
|
|
@@ -371,7 +417,7 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
371
417
|
return { buffer, metadata };
|
|
372
418
|
}
|
|
373
419
|
function sanitizeSVG(svg) {
|
|
374
|
-
return
|
|
420
|
+
return DOMPurify2__default.default.sanitize(svg, {
|
|
375
421
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
376
422
|
});
|
|
377
423
|
}
|
|
@@ -431,6 +477,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
431
477
|
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>`;
|
|
432
478
|
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>`;
|
|
433
479
|
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>`;
|
|
480
|
+
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>`;
|
|
481
|
+
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>`;
|
|
482
|
+
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>`;
|
|
434
483
|
var ICON_MAP = {
|
|
435
484
|
"zoom-in": ICON_ZOOM_IN,
|
|
436
485
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -451,7 +500,12 @@ var ICON_MAP = {
|
|
|
451
500
|
"page-prev": ICON_PAGE_PREV,
|
|
452
501
|
"page-next": ICON_PAGE_NEXT,
|
|
453
502
|
"chevron-left": ICON_PAGE_PREV,
|
|
454
|
-
"chevron-right": ICON_PAGE_NEXT
|
|
503
|
+
"chevron-right": ICON_PAGE_NEXT,
|
|
504
|
+
"fast-forward": ICON_FAST_FORWARD,
|
|
505
|
+
"forward-10": ICON_FAST_FORWARD,
|
|
506
|
+
"rewind": ICON_REWIND,
|
|
507
|
+
"replay-10": ICON_REWIND,
|
|
508
|
+
"speed": ICON_SPEED
|
|
455
509
|
};
|
|
456
510
|
var ToolbarController = class {
|
|
457
511
|
el;
|
|
@@ -1419,6 +1473,16 @@ var MediaPlugin = class {
|
|
|
1419
1473
|
}
|
|
1420
1474
|
if (instance.play) {
|
|
1421
1475
|
actions.push(
|
|
1476
|
+
{
|
|
1477
|
+
id: "replay-10",
|
|
1478
|
+
icon: "replay-10",
|
|
1479
|
+
label: "Rewind 10s",
|
|
1480
|
+
type: "button",
|
|
1481
|
+
group: "actions",
|
|
1482
|
+
execute: () => {
|
|
1483
|
+
instance.rewind?.(10);
|
|
1484
|
+
}
|
|
1485
|
+
},
|
|
1422
1486
|
{
|
|
1423
1487
|
id: "play",
|
|
1424
1488
|
icon: "play",
|
|
@@ -1438,6 +1502,26 @@ var MediaPlugin = class {
|
|
|
1438
1502
|
execute: () => {
|
|
1439
1503
|
instance.pause?.();
|
|
1440
1504
|
}
|
|
1505
|
+
},
|
|
1506
|
+
{
|
|
1507
|
+
id: "forward-10",
|
|
1508
|
+
icon: "forward-10",
|
|
1509
|
+
label: "Fast Forward 10s",
|
|
1510
|
+
type: "button",
|
|
1511
|
+
group: "actions",
|
|
1512
|
+
execute: () => {
|
|
1513
|
+
instance.fastForward?.(10);
|
|
1514
|
+
}
|
|
1515
|
+
},
|
|
1516
|
+
{
|
|
1517
|
+
id: "speed",
|
|
1518
|
+
icon: "speed",
|
|
1519
|
+
label: "Playback Speed",
|
|
1520
|
+
type: "button",
|
|
1521
|
+
group: "actions",
|
|
1522
|
+
execute: () => {
|
|
1523
|
+
instance.cycleSpeed?.();
|
|
1524
|
+
}
|
|
1441
1525
|
}
|
|
1442
1526
|
);
|
|
1443
1527
|
}
|
|
@@ -1560,6 +1644,28 @@ var MediaPlugin = class {
|
|
|
1560
1644
|
play: mediaElement ? () => mediaElement?.play() : void 0,
|
|
1561
1645
|
pause: mediaElement ? () => mediaElement?.pause() : void 0,
|
|
1562
1646
|
isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
|
|
1647
|
+
fastForward: mediaElement ? (seconds = 10) => {
|
|
1648
|
+
if (mediaElement) {
|
|
1649
|
+
mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
|
|
1650
|
+
}
|
|
1651
|
+
} : void 0,
|
|
1652
|
+
rewind: mediaElement ? (seconds = 10) => {
|
|
1653
|
+
if (mediaElement) {
|
|
1654
|
+
mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
|
|
1655
|
+
}
|
|
1656
|
+
} : void 0,
|
|
1657
|
+
cycleSpeed: mediaElement ? () => {
|
|
1658
|
+
if (mediaElement) {
|
|
1659
|
+
const speeds = [1, 1.25, 1.5, 2, 0.5];
|
|
1660
|
+
const curIndex = speeds.indexOf(mediaElement.playbackRate);
|
|
1661
|
+
const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
|
|
1662
|
+
mediaElement.playbackRate = speeds[nextIndex];
|
|
1663
|
+
}
|
|
1664
|
+
} : void 0,
|
|
1665
|
+
setPlaybackRate: mediaElement ? (rate) => {
|
|
1666
|
+
if (mediaElement) mediaElement.playbackRate = rate;
|
|
1667
|
+
} : void 0,
|
|
1668
|
+
getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
|
|
1563
1669
|
download: () => {
|
|
1564
1670
|
const a = document.createElement("a");
|
|
1565
1671
|
a.href = url;
|
|
@@ -1589,7 +1695,10 @@ var DocxPlugin = class {
|
|
|
1589
1695
|
supports(file) {
|
|
1590
1696
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1591
1697
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1592
|
-
|
|
1698
|
+
if (ext) {
|
|
1699
|
+
return this.extensions.includes(ext);
|
|
1700
|
+
}
|
|
1701
|
+
return this.mimeTypes.includes(mime || "");
|
|
1593
1702
|
}
|
|
1594
1703
|
getToolbarActions(instance) {
|
|
1595
1704
|
return [
|
|
@@ -1641,16 +1750,35 @@ var DocxPlugin = class {
|
|
|
1641
1750
|
wrapper.style.transformOrigin = "top center";
|
|
1642
1751
|
wrapper.style.transition = "transform 0.2s ease";
|
|
1643
1752
|
wrapper.style.padding = "24px 16px";
|
|
1644
|
-
wrapper.style.maxWidth = "
|
|
1753
|
+
wrapper.style.maxWidth = "950px";
|
|
1645
1754
|
wrapper.style.margin = "0 auto";
|
|
1646
1755
|
wrapper.style.boxSizing = "border-box";
|
|
1647
1756
|
ctx.container.style.overflow = "auto";
|
|
1648
1757
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
1649
1758
|
ctx.container.appendChild(wrapper);
|
|
1759
|
+
const styleOverride = document.createElement("style");
|
|
1760
|
+
styleOverride.textContent = `
|
|
1761
|
+
.fp-docx-wrapper .docx-wrapper {
|
|
1762
|
+
background: transparent !important;
|
|
1763
|
+
padding: 0 !important;
|
|
1764
|
+
display: flex !important;
|
|
1765
|
+
flex-direction: column !important;
|
|
1766
|
+
align-items: center !important;
|
|
1767
|
+
box-sizing: border-box !important;
|
|
1768
|
+
}
|
|
1769
|
+
.fp-docx-wrapper section.docx {
|
|
1770
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
|
|
1771
|
+
border-radius: 4px !important;
|
|
1772
|
+
margin-bottom: 24px !important;
|
|
1773
|
+
background-color: #ffffff !important;
|
|
1774
|
+
}
|
|
1775
|
+
`;
|
|
1776
|
+
ctx.container.appendChild(styleOverride);
|
|
1650
1777
|
let scale = 1;
|
|
1651
1778
|
let renderedSuccessfully = false;
|
|
1779
|
+
const createdBlobUrls = [];
|
|
1652
1780
|
try {
|
|
1653
|
-
await docx__namespace.renderAsync(ctx.buffer, wrapper,
|
|
1781
|
+
await docx__namespace.renderAsync(ctx.buffer, wrapper, wrapper, {
|
|
1654
1782
|
inWrapper: true,
|
|
1655
1783
|
ignoreWidth: false,
|
|
1656
1784
|
ignoreHeight: false,
|
|
@@ -1659,19 +1787,25 @@ var DocxPlugin = class {
|
|
|
1659
1787
|
breakPages: true,
|
|
1660
1788
|
experimental: true
|
|
1661
1789
|
});
|
|
1790
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1791
|
+
ctx.container.appendChild(wrapper);
|
|
1792
|
+
}
|
|
1662
1793
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
1663
1794
|
renderedSuccessfully = true;
|
|
1664
1795
|
}
|
|
1665
1796
|
} catch (err) {
|
|
1666
|
-
console.warn("[DocxPlugin] docx-preview failed, triggering native
|
|
1797
|
+
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
1667
1798
|
}
|
|
1668
1799
|
if (!renderedSuccessfully) {
|
|
1669
1800
|
try {
|
|
1670
1801
|
wrapper.innerHTML = "";
|
|
1671
|
-
this.renderXmlFallback(ctx, wrapper);
|
|
1802
|
+
this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
|
|
1803
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1804
|
+
ctx.container.appendChild(wrapper);
|
|
1805
|
+
}
|
|
1672
1806
|
renderedSuccessfully = true;
|
|
1673
1807
|
} catch (fallbackErr) {
|
|
1674
|
-
console.error("[DocxPlugin]
|
|
1808
|
+
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
1675
1809
|
wrapper.innerHTML = `
|
|
1676
1810
|
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
1677
1811
|
<div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
|
|
@@ -1679,10 +1813,17 @@ var DocxPlugin = class {
|
|
|
1679
1813
|
<p style="color: #64748b; margin: 0;">Could not parse document content</p>
|
|
1680
1814
|
</div>
|
|
1681
1815
|
`;
|
|
1816
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1817
|
+
ctx.container.appendChild(wrapper);
|
|
1818
|
+
}
|
|
1682
1819
|
}
|
|
1683
1820
|
}
|
|
1684
1821
|
const cleanup = () => {
|
|
1822
|
+
for (const url of createdBlobUrls) {
|
|
1823
|
+
URL.revokeObjectURL(url);
|
|
1824
|
+
}
|
|
1685
1825
|
wrapper.remove();
|
|
1826
|
+
styleOverride.remove();
|
|
1686
1827
|
ctx.container.innerHTML = "";
|
|
1687
1828
|
};
|
|
1688
1829
|
ctx.signal.addEventListener("abort", cleanup);
|
|
@@ -1719,13 +1860,50 @@ var DocxPlugin = class {
|
|
|
1719
1860
|
}
|
|
1720
1861
|
};
|
|
1721
1862
|
}
|
|
1722
|
-
renderXmlFallback(ctx, wrapper) {
|
|
1863
|
+
renderXmlFallback(ctx, wrapper, createdBlobUrls) {
|
|
1864
|
+
const magic = new Uint8Array(ctx.buffer.slice(0, 8));
|
|
1865
|
+
if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
|
|
1866
|
+
this.renderBinaryDocFallback(ctx, wrapper);
|
|
1867
|
+
return;
|
|
1868
|
+
}
|
|
1723
1869
|
const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
|
|
1724
|
-
const
|
|
1870
|
+
const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
|
|
1871
|
+
const docXmlEntry = docKey ? unzipped[docKey] : void 0;
|
|
1725
1872
|
if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
|
|
1726
1873
|
const xmlStr = fflate.strFromU8(docXmlEntry);
|
|
1727
1874
|
const parser = new DOMParser();
|
|
1728
1875
|
const doc = parser.parseFromString(xmlStr, "application/xml");
|
|
1876
|
+
const imageMap = {};
|
|
1877
|
+
const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
|
|
1878
|
+
if (relsKey && unzipped[relsKey]) {
|
|
1879
|
+
try {
|
|
1880
|
+
const relsXml = fflate.strFromU8(unzipped[relsKey]);
|
|
1881
|
+
const relsDoc = parser.parseFromString(relsXml, "application/xml");
|
|
1882
|
+
const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
|
|
1883
|
+
for (const rel of relEls) {
|
|
1884
|
+
const rId = rel.getAttribute("Id");
|
|
1885
|
+
const target = rel.getAttribute("Target");
|
|
1886
|
+
if (rId && target) {
|
|
1887
|
+
const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
|
|
1888
|
+
const fullTargetKey = Object.keys(unzipped).find((k) => {
|
|
1889
|
+
const norm = k.replace(/^[./\\]+/, "").toLowerCase();
|
|
1890
|
+
return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
|
|
1891
|
+
});
|
|
1892
|
+
if (fullTargetKey && unzipped[fullTargetKey]) {
|
|
1893
|
+
const bytes = unzipped[fullTargetKey];
|
|
1894
|
+
const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
|
|
1895
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
|
|
1896
|
+
const blob = new Blob([bytes], { type: mime });
|
|
1897
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1898
|
+
imageMap[rId] = blobUrl;
|
|
1899
|
+
createdBlobUrls.push(blobUrl);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
} catch (relsErr) {
|
|
1904
|
+
console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1729
1907
|
const card = document.createElement("div");
|
|
1730
1908
|
card.className = "fp-docx-page-card";
|
|
1731
1909
|
card.style.backgroundColor = "#ffffff";
|
|
@@ -1743,7 +1921,7 @@ var DocxPlugin = class {
|
|
|
1743
1921
|
const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
|
|
1744
1922
|
const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
|
|
1745
1923
|
const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
|
|
1746
|
-
const textContent = this.extractParagraphHtml(child);
|
|
1924
|
+
const textContent = this.extractParagraphHtml(child, imageMap);
|
|
1747
1925
|
if (!textContent.trim()) {
|
|
1748
1926
|
html += '<div style="height: 10px;"></div>';
|
|
1749
1927
|
continue;
|
|
@@ -1769,7 +1947,7 @@ var DocxPlugin = class {
|
|
|
1769
1947
|
html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
1770
1948
|
const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
|
|
1771
1949
|
cells.forEach((tc) => {
|
|
1772
|
-
const cellText = this.extractParagraphHtml(tc);
|
|
1950
|
+
const cellText = this.extractParagraphHtml(tc, imageMap);
|
|
1773
1951
|
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || " "}</td>`;
|
|
1774
1952
|
});
|
|
1775
1953
|
html += "</tr>";
|
|
@@ -1777,34 +1955,63 @@ var DocxPlugin = class {
|
|
|
1777
1955
|
html += "</table>";
|
|
1778
1956
|
}
|
|
1779
1957
|
}
|
|
1780
|
-
card.innerHTML =
|
|
1781
|
-
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
|
|
1958
|
+
card.innerHTML = DOMPurify2__default.default.sanitize(html, {
|
|
1959
|
+
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
|
|
1782
1960
|
ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
|
|
1783
1961
|
});
|
|
1784
1962
|
wrapper.appendChild(card);
|
|
1785
1963
|
}
|
|
1786
|
-
extractParagraphHtml(pElement) {
|
|
1964
|
+
extractParagraphHtml(pElement, imageMap = {}) {
|
|
1787
1965
|
let result = "";
|
|
1966
|
+
const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
|
|
1967
|
+
for (const drawing of drawings) {
|
|
1968
|
+
const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
|
|
1969
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1970
|
+
if (rId && imageMap[rId]) {
|
|
1971
|
+
result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1788
1974
|
const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
|
|
1789
|
-
if (runs.length === 0) {
|
|
1790
|
-
return
|
|
1975
|
+
if (runs.length === 0 && !result) {
|
|
1976
|
+
return DOMPurify2__default.default.sanitize(pElement.textContent || "");
|
|
1791
1977
|
}
|
|
1792
1978
|
for (const r of runs) {
|
|
1979
|
+
const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
|
|
1980
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1981
|
+
if (rId && imageMap[rId]) {
|
|
1982
|
+
result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
|
|
1983
|
+
}
|
|
1984
|
+
const brs = r.getElementsByTagNameNS("*", "br");
|
|
1985
|
+
for (let i = 0; i < brs.length; i++) {
|
|
1986
|
+
result += "<br/>";
|
|
1987
|
+
}
|
|
1988
|
+
const tabs = r.getElementsByTagNameNS("*", "tab");
|
|
1989
|
+
if (tabs.length > 0) {
|
|
1990
|
+
result += " ";
|
|
1991
|
+
}
|
|
1793
1992
|
const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
|
|
1794
1993
|
const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
|
|
1795
1994
|
const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
|
|
1796
1995
|
const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
|
|
1996
|
+
const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
|
|
1797
1997
|
const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
|
|
1798
1998
|
const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
|
|
1999
|
+
const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
|
|
2000
|
+
const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
|
|
1799
2001
|
const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
|
|
1800
2002
|
let text = texts.map((t) => t.textContent || "").join("");
|
|
1801
2003
|
if (!text) continue;
|
|
1802
|
-
text =
|
|
2004
|
+
text = DOMPurify2__default.default.sanitize(text);
|
|
1803
2005
|
let styles = "";
|
|
1804
2006
|
if (isBold) styles += "font-weight: bold; ";
|
|
1805
2007
|
if (isItalic) styles += "font-style: italic; ";
|
|
1806
2008
|
if (isUnderline) styles += "text-decoration: underline; ";
|
|
2009
|
+
if (isStrike) styles += "text-decoration: line-through; ";
|
|
1807
2010
|
if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
|
|
2011
|
+
if (szVal) {
|
|
2012
|
+
const pt = parseInt(szVal, 10) / 2;
|
|
2013
|
+
if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
|
|
2014
|
+
}
|
|
1808
2015
|
if (styles) {
|
|
1809
2016
|
result += `<span style="${styles}">${text}</span>`;
|
|
1810
2017
|
} else {
|
|
@@ -1813,6 +2020,52 @@ var DocxPlugin = class {
|
|
|
1813
2020
|
}
|
|
1814
2021
|
return result;
|
|
1815
2022
|
}
|
|
2023
|
+
renderBinaryDocFallback(ctx, wrapper) {
|
|
2024
|
+
const card = document.createElement("div");
|
|
2025
|
+
card.className = "fp-docx-page-card";
|
|
2026
|
+
card.style.backgroundColor = "#ffffff";
|
|
2027
|
+
card.style.borderRadius = "6px";
|
|
2028
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
2029
|
+
card.style.padding = "48px 56px";
|
|
2030
|
+
card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
2031
|
+
card.style.color = "#1e293b";
|
|
2032
|
+
card.style.lineHeight = "1.6";
|
|
2033
|
+
try {
|
|
2034
|
+
const cfbf = new CfbfReader(ctx.buffer);
|
|
2035
|
+
const wordDocStream = cfbf.readStream("WordDocument");
|
|
2036
|
+
if (wordDocStream && wordDocStream.length >= 512) {
|
|
2037
|
+
const text2 = this.extractReadableStrings(wordDocStream);
|
|
2038
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text2)}</div>`;
|
|
2039
|
+
wrapper.appendChild(card);
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
} catch {
|
|
2043
|
+
}
|
|
2044
|
+
const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
|
|
2045
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text)}</div>`;
|
|
2046
|
+
wrapper.appendChild(card);
|
|
2047
|
+
}
|
|
2048
|
+
extractReadableStrings(bytes) {
|
|
2049
|
+
let result = "";
|
|
2050
|
+
let current = "";
|
|
2051
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2052
|
+
const b = bytes[i];
|
|
2053
|
+
if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
|
|
2054
|
+
current += String.fromCharCode(b);
|
|
2055
|
+
} else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
|
|
2056
|
+
continue;
|
|
2057
|
+
} else {
|
|
2058
|
+
if (current.trim().length > 3) {
|
|
2059
|
+
result += current.trim() + "\n\n";
|
|
2060
|
+
}
|
|
2061
|
+
current = "";
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
if (current.trim().length > 3) {
|
|
2065
|
+
result += current.trim();
|
|
2066
|
+
}
|
|
2067
|
+
return result;
|
|
2068
|
+
}
|
|
1816
2069
|
};
|
|
1817
2070
|
function docxPlugin() {
|
|
1818
2071
|
return new DocxPlugin();
|
|
@@ -1834,7 +2087,10 @@ var ExcelPlugin = class {
|
|
|
1834
2087
|
supports(file) {
|
|
1835
2088
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1836
2089
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1837
|
-
|
|
2090
|
+
if (ext) {
|
|
2091
|
+
return this.extensions.includes(ext);
|
|
2092
|
+
}
|
|
2093
|
+
return this.mimeTypes.includes(mime || "");
|
|
1838
2094
|
}
|
|
1839
2095
|
getToolbarActions(instance) {
|
|
1840
2096
|
return [
|
|
@@ -2624,7 +2880,7 @@ var MarkdownPlugin = class {
|
|
|
2624
2880
|
gfm: true,
|
|
2625
2881
|
breaks: true
|
|
2626
2882
|
});
|
|
2627
|
-
const cleanHtml =
|
|
2883
|
+
const cleanHtml = DOMPurify2__default.default.sanitize(rawHtml, {
|
|
2628
2884
|
USE_PROFILES: { html: true }
|
|
2629
2885
|
});
|
|
2630
2886
|
const wrapper = document.createElement("div");
|
|
@@ -2776,7 +3032,10 @@ var PptxPlugin = class {
|
|
|
2776
3032
|
supports(file) {
|
|
2777
3033
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2778
3034
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2779
|
-
|
|
3035
|
+
if (ext) {
|
|
3036
|
+
return this.extensions.includes(ext);
|
|
3037
|
+
}
|
|
3038
|
+
return this.mimeTypes.includes(mime || "");
|
|
2780
3039
|
}
|
|
2781
3040
|
getToolbarActions(instance) {
|
|
2782
3041
|
return [
|
|
@@ -3221,7 +3480,10 @@ var RtfPlugin = class {
|
|
|
3221
3480
|
ctx.container.appendChild(wrapper);
|
|
3222
3481
|
let scale = 1;
|
|
3223
3482
|
try {
|
|
3224
|
-
|
|
3483
|
+
if (typeof RTFJS__namespace.loggingEnabled === "function") {
|
|
3484
|
+
RTFJS__namespace.loggingEnabled(false);
|
|
3485
|
+
}
|
|
3486
|
+
const doc = new RTFJS__namespace.Document(ctx.buffer, {});
|
|
3225
3487
|
const htmlElements = await doc.render();
|
|
3226
3488
|
for (const el of htmlElements) {
|
|
3227
3489
|
wrapper.appendChild(el);
|
|
@@ -3339,7 +3601,7 @@ var HtmlPreviewPlugin = class {
|
|
|
3339
3601
|
}
|
|
3340
3602
|
async render(ctx) {
|
|
3341
3603
|
const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
|
|
3342
|
-
const sanitized =
|
|
3604
|
+
const sanitized = DOMPurify2__default.default.sanitize(rawHtml, {
|
|
3343
3605
|
WHOLE_DOCUMENT: true,
|
|
3344
3606
|
ADD_TAGS: ["style", "link"],
|
|
3345
3607
|
ADD_ATTR: ["target", "rel"]
|
|
@@ -3419,7 +3681,10 @@ var OpenDocumentPlugin = class {
|
|
|
3419
3681
|
supports(file) {
|
|
3420
3682
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3421
3683
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3422
|
-
|
|
3684
|
+
if (ext) {
|
|
3685
|
+
return this.extensions.includes(ext);
|
|
3686
|
+
}
|
|
3687
|
+
return this.mimeTypes.includes(mime || "");
|
|
3423
3688
|
}
|
|
3424
3689
|
getToolbarActions(instance) {
|
|
3425
3690
|
const isPresentation = instance.isPresentation;
|
|
@@ -3582,7 +3847,7 @@ var OpenDocumentPlugin = class {
|
|
|
3582
3847
|
wrapper.style.padding = "48px";
|
|
3583
3848
|
wrapper.style.minHeight = "100%";
|
|
3584
3849
|
const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
|
|
3585
|
-
wrapper.innerHTML =
|
|
3850
|
+
wrapper.innerHTML = DOMPurify2__default.default.sanitize(bodyHtml, {
|
|
3586
3851
|
ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
|
|
3587
3852
|
ADD_ATTR: ["style", "colspan", "rowspan"]
|
|
3588
3853
|
});
|
|
@@ -3834,7 +4099,10 @@ var DocPlugin = class {
|
|
|
3834
4099
|
supports(file) {
|
|
3835
4100
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3836
4101
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3837
|
-
|
|
4102
|
+
if (ext) {
|
|
4103
|
+
return this.extensions.includes(ext);
|
|
4104
|
+
}
|
|
4105
|
+
return this.mimeTypes.includes(mime || "");
|
|
3838
4106
|
}
|
|
3839
4107
|
getToolbarActions(instance) {
|
|
3840
4108
|
return [
|
|
@@ -4057,22 +4325,22 @@ var DocPlugin = class {
|
|
|
4057
4325
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4058
4326
|
*/
|
|
4059
4327
|
extractStringsFromBytes(bytes) {
|
|
4060
|
-
const
|
|
4061
|
-
const
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4328
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4329
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4330
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4331
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4332
|
+
const candidateLines = [];
|
|
4333
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4334
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4335
|
+
const trimmed = run.trim();
|
|
4336
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4337
|
+
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
4338
|
+
seen.add(trimmed);
|
|
4339
|
+
candidateLines.push(trimmed);
|
|
4340
|
+
}
|
|
4073
4341
|
}
|
|
4074
4342
|
}
|
|
4075
|
-
return
|
|
4343
|
+
return candidateLines.join("\n\n");
|
|
4076
4344
|
}
|
|
4077
4345
|
heuristicTextExtraction(buffer) {
|
|
4078
4346
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4102,24 +4370,24 @@ var DocPlugin = class {
|
|
|
4102
4370
|
html += "</ul>";
|
|
4103
4371
|
inList = false;
|
|
4104
4372
|
}
|
|
4105
|
-
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${
|
|
4373
|
+
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2__default.default.sanitize(line)}</h2>`;
|
|
4106
4374
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
4107
4375
|
if (!inList) {
|
|
4108
4376
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
4109
4377
|
inList = true;
|
|
4110
4378
|
}
|
|
4111
4379
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
4112
|
-
html += `<li style="margin: 4px 0; line-height: 1.6;">${
|
|
4380
|
+
html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2__default.default.sanitize(bulletText)}</li>`;
|
|
4113
4381
|
} else {
|
|
4114
4382
|
if (inList) {
|
|
4115
4383
|
html += "</ul>";
|
|
4116
4384
|
inList = false;
|
|
4117
4385
|
}
|
|
4118
|
-
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${
|
|
4386
|
+
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2__default.default.sanitize(line)}</p>`;
|
|
4119
4387
|
}
|
|
4120
4388
|
}
|
|
4121
4389
|
if (inList) html += "</ul>";
|
|
4122
|
-
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${
|
|
4390
|
+
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2__default.default.sanitize(filename)})</p>`;
|
|
4123
4391
|
}
|
|
4124
4392
|
};
|
|
4125
4393
|
function docPlugin() {
|
|
@@ -4127,14 +4395,17 @@ function docPlugin() {
|
|
|
4127
4395
|
}
|
|
4128
4396
|
var PptPlugin = class {
|
|
4129
4397
|
id = "ppt";
|
|
4130
|
-
name = "
|
|
4398
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4131
4399
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4132
4400
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4133
|
-
weight =
|
|
4401
|
+
weight = 85;
|
|
4134
4402
|
supports(file) {
|
|
4135
4403
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4136
4404
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4137
|
-
|
|
4405
|
+
if (ext) {
|
|
4406
|
+
return this.extensions.includes(ext);
|
|
4407
|
+
}
|
|
4408
|
+
return this.mimeTypes.includes(mime || "");
|
|
4138
4409
|
}
|
|
4139
4410
|
getToolbarActions(instance) {
|
|
4140
4411
|
return [
|
|
@@ -4222,19 +4493,15 @@ var PptPlugin = class {
|
|
|
4222
4493
|
container.style.alignItems = "center";
|
|
4223
4494
|
container.style.padding = "32px 16px";
|
|
4224
4495
|
container.style.backgroundColor = "#0f172a";
|
|
4496
|
+
container.style.boxSizing = "border-box";
|
|
4225
4497
|
const slideCard = document.createElement("div");
|
|
4226
4498
|
slideCard.className = "fp-ppt-slide-card";
|
|
4227
4499
|
slideCard.style.width = "960px";
|
|
4228
|
-
slideCard.style.maxWidth = "
|
|
4500
|
+
slideCard.style.maxWidth = "92%";
|
|
4229
4501
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4230
4502
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4231
|
-
slideCard.style.boxShadow = "0
|
|
4503
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4232
4504
|
slideCard.style.borderRadius = "8px";
|
|
4233
|
-
slideCard.style.padding = "48px";
|
|
4234
|
-
slideCard.style.display = "flex";
|
|
4235
|
-
slideCard.style.flexDirection = "column";
|
|
4236
|
-
slideCard.style.justifyContent = "center";
|
|
4237
|
-
slideCard.style.alignItems = "center";
|
|
4238
4505
|
slideCard.style.boxSizing = "border-box";
|
|
4239
4506
|
slideCard.style.position = "relative";
|
|
4240
4507
|
slideCard.style.overflow = "hidden";
|
|
@@ -4245,21 +4512,25 @@ var PptPlugin = class {
|
|
|
4245
4512
|
let scale = 1;
|
|
4246
4513
|
let currentSlide = 1;
|
|
4247
4514
|
let slides = [];
|
|
4515
|
+
const createdBlobUrls = [];
|
|
4248
4516
|
try {
|
|
4249
4517
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4250
4518
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4251
4519
|
if (!pptStream || pptStream.length < 512) {
|
|
4252
4520
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4253
4521
|
}
|
|
4254
|
-
|
|
4522
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4523
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4255
4524
|
} catch (err) {
|
|
4256
4525
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4257
4526
|
}
|
|
4258
4527
|
if (slides.length === 0) {
|
|
4259
4528
|
slides = [
|
|
4260
4529
|
{
|
|
4530
|
+
slideIndex: 1,
|
|
4261
4531
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4262
|
-
|
|
4532
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4533
|
+
tableColumns: []
|
|
4263
4534
|
}
|
|
4264
4535
|
];
|
|
4265
4536
|
}
|
|
@@ -4268,23 +4539,73 @@ var PptPlugin = class {
|
|
|
4268
4539
|
currentSlide = idx;
|
|
4269
4540
|
const s = slides[idx - 1];
|
|
4270
4541
|
if (!s) return;
|
|
4542
|
+
let contentHtml = "";
|
|
4543
|
+
if (s.pictureUrl) {
|
|
4544
|
+
contentHtml = `
|
|
4545
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4546
|
+
<img src="${s.pictureUrl}" alt="${DOMPurify2__default.default.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
|
|
4547
|
+
</div>
|
|
4548
|
+
`;
|
|
4549
|
+
} else if (s.tableColumns.length > 0) {
|
|
4550
|
+
const cols = s.tableColumns;
|
|
4551
|
+
contentHtml = `
|
|
4552
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4553
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4554
|
+
<thead>
|
|
4555
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4556
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2__default.default.sanitize(c)}</th>`).join("")}
|
|
4557
|
+
</tr>
|
|
4558
|
+
</thead>
|
|
4559
|
+
<tbody>
|
|
4560
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4561
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4562
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4563
|
+
</tr>
|
|
4564
|
+
`).join("")}
|
|
4565
|
+
</tbody>
|
|
4566
|
+
</table>
|
|
4567
|
+
</div>
|
|
4568
|
+
`;
|
|
4569
|
+
} else {
|
|
4570
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4571
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4572
|
+
return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2__default.default.sanitize(l)}</p>`).join("");
|
|
4573
|
+
}).join("");
|
|
4574
|
+
contentHtml = `
|
|
4575
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4576
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4577
|
+
</div>
|
|
4578
|
+
`;
|
|
4579
|
+
}
|
|
4271
4580
|
slideCard.innerHTML = `
|
|
4272
|
-
<div style="
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4581
|
+
<div style="width: 100%; height: 100%; background: #ffffff; border: 14px solid #334155; box-sizing: border-box; display: flex; flex-direction: column; padding: 24px 32px; position: relative; font-family: Calibri, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; overflow: hidden; border-radius: 4px;">
|
|
4582
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4583
|
+
<div style="background: linear-gradient(90deg, #a3e635 0%, #84cc16 100%); padding: 12px 24px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); flex-shrink: 0;">
|
|
4584
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4585
|
+
${DOMPurify2__default.default.sanitize(s.title)}
|
|
4586
|
+
</h1>
|
|
4587
|
+
${s.subtitle ? `
|
|
4588
|
+
<span style="background: #38bdf8; color: #ffffff; padding: 4px 14px; border-radius: 4px; font-weight: 700; font-size: 13px; letter-spacing: 0.5px; box-shadow: 0 1px 4px rgba(0,0,0,0.15);">
|
|
4589
|
+
${DOMPurify2__default.default.sanitize(s.subtitle)}
|
|
4590
|
+
</span>
|
|
4591
|
+
` : `
|
|
4592
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4593
|
+
Slide ${idx} / ${totalSlides}
|
|
4594
|
+
</span>
|
|
4595
|
+
`}
|
|
4281
4596
|
</div>
|
|
4597
|
+
|
|
4598
|
+
<!-- Slide Content -->
|
|
4599
|
+
${contentHtml}
|
|
4282
4600
|
</div>
|
|
4283
4601
|
`;
|
|
4284
4602
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4285
4603
|
};
|
|
4286
4604
|
renderSlide(1);
|
|
4287
4605
|
const cleanup = () => {
|
|
4606
|
+
for (const u of createdBlobUrls) {
|
|
4607
|
+
URL.revokeObjectURL(u);
|
|
4608
|
+
}
|
|
4288
4609
|
container.remove();
|
|
4289
4610
|
ctx.container.innerHTML = "";
|
|
4290
4611
|
};
|
|
@@ -4324,13 +4645,48 @@ var PptPlugin = class {
|
|
|
4324
4645
|
if (!ctx2d) return;
|
|
4325
4646
|
canvas.width = 160;
|
|
4326
4647
|
canvas.height = 90;
|
|
4327
|
-
ctx2d.fillStyle = "#
|
|
4648
|
+
ctx2d.fillStyle = "#334155";
|
|
4328
4649
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4329
|
-
ctx2d.fillStyle = "#
|
|
4330
|
-
ctx2d.
|
|
4331
|
-
ctx2d.
|
|
4332
|
-
|
|
4333
|
-
ctx2d.
|
|
4650
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4651
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4652
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4653
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4654
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4655
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4656
|
+
ctx2d.textAlign = "left";
|
|
4657
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4658
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4659
|
+
if (s.subtitle) {
|
|
4660
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4661
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4662
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4663
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4664
|
+
ctx2d.textAlign = "center";
|
|
4665
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4666
|
+
}
|
|
4667
|
+
if (s.pictureUrl) {
|
|
4668
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4669
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4670
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4671
|
+
ctx2d.font = "8px sans-serif";
|
|
4672
|
+
ctx2d.textAlign = "center";
|
|
4673
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4674
|
+
} else if (s.tableColumns.length > 0) {
|
|
4675
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4676
|
+
ctx2d.lineWidth = 1;
|
|
4677
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4678
|
+
for (let l = 1; l <= 3; l++) {
|
|
4679
|
+
ctx2d.beginPath();
|
|
4680
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4681
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4682
|
+
ctx2d.stroke();
|
|
4683
|
+
}
|
|
4684
|
+
} else {
|
|
4685
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4686
|
+
for (let l = 0; l < 4; l++) {
|
|
4687
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4334
4690
|
}
|
|
4335
4691
|
}));
|
|
4336
4692
|
},
|
|
@@ -4348,66 +4704,165 @@ var PptPlugin = class {
|
|
|
4348
4704
|
}
|
|
4349
4705
|
};
|
|
4350
4706
|
}
|
|
4707
|
+
/**
|
|
4708
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4709
|
+
*/
|
|
4710
|
+
extractPictures(cfbf, createdUrls) {
|
|
4711
|
+
const urls = [];
|
|
4712
|
+
try {
|
|
4713
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4714
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4715
|
+
const pBuf = new Uint8Array(picStream);
|
|
4716
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4717
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4718
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4719
|
+
let match = true;
|
|
4720
|
+
for (let j = 0; j < 8; j++) {
|
|
4721
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4722
|
+
match = false;
|
|
4723
|
+
break;
|
|
4724
|
+
}
|
|
4725
|
+
}
|
|
4726
|
+
if (match) {
|
|
4727
|
+
let endIdx = -1;
|
|
4728
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4729
|
+
let endMatch = true;
|
|
4730
|
+
for (let j = 0; j < 8; j++) {
|
|
4731
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4732
|
+
endMatch = false;
|
|
4733
|
+
break;
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
if (endMatch) {
|
|
4737
|
+
endIdx = k + 8;
|
|
4738
|
+
break;
|
|
4739
|
+
}
|
|
4740
|
+
}
|
|
4741
|
+
if (endIdx !== -1) {
|
|
4742
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4743
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4744
|
+
const url = URL.createObjectURL(blob);
|
|
4745
|
+
urls.push(url);
|
|
4746
|
+
createdUrls.push(url);
|
|
4747
|
+
i = endIdx;
|
|
4748
|
+
}
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4752
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4753
|
+
let endIdx = -1;
|
|
4754
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4755
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4756
|
+
endIdx = k + 2;
|
|
4757
|
+
break;
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
if (endIdx !== -1) {
|
|
4761
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4762
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4763
|
+
const url = URL.createObjectURL(blob);
|
|
4764
|
+
urls.push(url);
|
|
4765
|
+
createdUrls.push(url);
|
|
4766
|
+
i = endIdx;
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
} catch (err) {
|
|
4771
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4772
|
+
}
|
|
4773
|
+
return urls;
|
|
4774
|
+
}
|
|
4351
4775
|
/**
|
|
4352
4776
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4353
4777
|
*/
|
|
4354
|
-
extractSlides(stream) {
|
|
4778
|
+
extractSlides(stream, pictures) {
|
|
4355
4779
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4356
4780
|
const len = stream.length;
|
|
4357
4781
|
let offset = 0;
|
|
4358
4782
|
const slides = [];
|
|
4359
|
-
let
|
|
4783
|
+
let picIdx = 0;
|
|
4360
4784
|
while (offset + 8 <= len) {
|
|
4361
4785
|
const recVerInst = view.getUint16(offset, true);
|
|
4362
4786
|
const recType = view.getUint16(offset + 2, true);
|
|
4363
4787
|
const recLen = view.getUint32(offset + 4, true);
|
|
4788
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4364
4789
|
if (recType === 1006) {
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4790
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4791
|
+
const rawTexts = [];
|
|
4792
|
+
let hasOle = false;
|
|
4793
|
+
let sOff = offset + 8;
|
|
4794
|
+
while (sOff + 8 <= slideEnd) {
|
|
4795
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4796
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4797
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4798
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4799
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4800
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4801
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4802
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4803
|
+
rawTexts.push(txt);
|
|
4804
|
+
}
|
|
4805
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4806
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4807
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4808
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4809
|
+
rawTexts.push(txt);
|
|
4810
|
+
}
|
|
4811
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4812
|
+
hasOle = true;
|
|
4813
|
+
}
|
|
4814
|
+
if (cIsContainer) sOff += 8;
|
|
4815
|
+
else sOff += 8 + cLen;
|
|
4370
4816
|
}
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4817
|
+
let title = "";
|
|
4818
|
+
let subtitle = "";
|
|
4819
|
+
const paragraphs = [];
|
|
4820
|
+
const tableColumns = [];
|
|
4821
|
+
for (const t of rawTexts) {
|
|
4822
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4823
|
+
title = t;
|
|
4824
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4825
|
+
tableColumns.push(t);
|
|
4826
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4827
|
+
subtitle = t;
|
|
4828
|
+
} else {
|
|
4829
|
+
paragraphs.push(t);
|
|
4830
|
+
}
|
|
4379
4831
|
}
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4384
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4385
|
-
currentSlideTexts.push(text);
|
|
4832
|
+
let pictureUrl = null;
|
|
4833
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4834
|
+
pictureUrl = pictures[picIdx++];
|
|
4386
4835
|
}
|
|
4836
|
+
slides.push({
|
|
4837
|
+
slideIndex: slides.length + 1,
|
|
4838
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4839
|
+
subtitle,
|
|
4840
|
+
paragraphs,
|
|
4841
|
+
tableColumns,
|
|
4842
|
+
pictureUrl,
|
|
4843
|
+
hasOle
|
|
4844
|
+
});
|
|
4387
4845
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
offset += 8;
|
|
4391
|
-
} else {
|
|
4392
|
-
offset += 8 + recLen;
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
if (currentSlideTexts.length > 0) {
|
|
4396
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4397
|
-
const texts = currentSlideTexts.slice(1);
|
|
4398
|
-
slides.push({ title, texts });
|
|
4846
|
+
if (isContainer) offset += 8;
|
|
4847
|
+
else offset += 8 + recLen;
|
|
4399
4848
|
}
|
|
4400
4849
|
if (slides.length === 0) {
|
|
4401
4850
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4402
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4403
|
-
const
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4851
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4852
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4853
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4854
|
+
);
|
|
4855
|
+
if (clean.length > 0) {
|
|
4856
|
+
const chunkSize = 3;
|
|
4857
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4858
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4408
4859
|
slides.push({
|
|
4860
|
+
slideIndex: slides.length + 1,
|
|
4409
4861
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4410
|
-
|
|
4862
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4863
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4864
|
+
tableColumns: [],
|
|
4865
|
+
pictureUrl: pictures[slides.length] || null
|
|
4411
4866
|
});
|
|
4412
4867
|
}
|
|
4413
4868
|
}
|