@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/angular.cjs
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
4
4
|
|
|
5
5
|
var core = require('@angular/core');
|
|
6
6
|
var common = require('@angular/common');
|
|
7
|
-
var
|
|
7
|
+
var DOMPurify2 = require('dompurify');
|
|
8
8
|
var docx = require('docx-preview');
|
|
9
9
|
var fflate = require('fflate');
|
|
10
10
|
var XLSX = require('xlsx');
|
|
@@ -15,7 +15,7 @@ var THREE = require('three');
|
|
|
15
15
|
var STLLoader_js = require('three/examples/jsm/loaders/STLLoader.js');
|
|
16
16
|
var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
|
|
17
17
|
var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
|
|
18
|
-
var
|
|
18
|
+
var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
|
|
19
19
|
|
|
20
20
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
21
21
|
|
|
@@ -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
|
var __create = Object.create;
|
|
47
48
|
var __defProp = Object.defineProperty;
|
|
@@ -344,6 +345,39 @@ function detectOoxmlType(buffer) {
|
|
|
344
345
|
}
|
|
345
346
|
return "application/zip";
|
|
346
347
|
}
|
|
348
|
+
function detectCfbfType(buffer) {
|
|
349
|
+
const bytes = new Uint8Array(buffer);
|
|
350
|
+
const hasUtf16le = (str) => {
|
|
351
|
+
const target = new Uint8Array(str.length * 2);
|
|
352
|
+
for (let i = 0; i < str.length; i++) {
|
|
353
|
+
target[i * 2] = str.charCodeAt(i);
|
|
354
|
+
target[i * 2 + 1] = 0;
|
|
355
|
+
}
|
|
356
|
+
const targetLen = target.length;
|
|
357
|
+
const max = bytes.length - targetLen;
|
|
358
|
+
for (let i = 0; i <= max; i++) {
|
|
359
|
+
let match = true;
|
|
360
|
+
for (let j = 0; j < targetLen; j++) {
|
|
361
|
+
if (bytes[i + j] !== target[j]) {
|
|
362
|
+
match = false;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (match) return true;
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
};
|
|
370
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
371
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
372
|
+
}
|
|
373
|
+
if (hasUtf16le("WordDocument")) {
|
|
374
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
375
|
+
}
|
|
376
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
377
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
378
|
+
}
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
347
381
|
function extractExtension(nameOrUrl) {
|
|
348
382
|
try {
|
|
349
383
|
const url = new URL(nameOrUrl);
|
|
@@ -408,6 +442,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
408
442
|
} else {
|
|
409
443
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
410
444
|
}
|
|
445
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
446
|
+
if (metadata.extension) {
|
|
447
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
448
|
+
} else {
|
|
449
|
+
const cfbf = detectCfbfType(buffer);
|
|
450
|
+
if (cfbf) {
|
|
451
|
+
metadata.mimeType = cfbf.mime;
|
|
452
|
+
metadata.extension = cfbf.extension;
|
|
453
|
+
} else {
|
|
454
|
+
metadata.mimeType = magicMime;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
411
457
|
} else {
|
|
412
458
|
metadata.mimeType = magicMime;
|
|
413
459
|
}
|
|
@@ -418,7 +464,7 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
418
464
|
return { buffer, metadata };
|
|
419
465
|
}
|
|
420
466
|
function sanitizeSVG(svg) {
|
|
421
|
-
return
|
|
467
|
+
return DOMPurify2__default.default.sanitize(svg, {
|
|
422
468
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
423
469
|
});
|
|
424
470
|
}
|
|
@@ -478,6 +524,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
478
524
|
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>`;
|
|
479
525
|
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>`;
|
|
480
526
|
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>`;
|
|
527
|
+
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>`;
|
|
528
|
+
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>`;
|
|
529
|
+
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>`;
|
|
481
530
|
var ICON_MAP = {
|
|
482
531
|
"zoom-in": ICON_ZOOM_IN,
|
|
483
532
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -498,7 +547,12 @@ var ICON_MAP = {
|
|
|
498
547
|
"page-prev": ICON_PAGE_PREV,
|
|
499
548
|
"page-next": ICON_PAGE_NEXT,
|
|
500
549
|
"chevron-left": ICON_PAGE_PREV,
|
|
501
|
-
"chevron-right": ICON_PAGE_NEXT
|
|
550
|
+
"chevron-right": ICON_PAGE_NEXT,
|
|
551
|
+
"fast-forward": ICON_FAST_FORWARD,
|
|
552
|
+
"forward-10": ICON_FAST_FORWARD,
|
|
553
|
+
"rewind": ICON_REWIND,
|
|
554
|
+
"replay-10": ICON_REWIND,
|
|
555
|
+
"speed": ICON_SPEED
|
|
502
556
|
};
|
|
503
557
|
var ToolbarController = class {
|
|
504
558
|
el;
|
|
@@ -1466,6 +1520,16 @@ var MediaPlugin = class {
|
|
|
1466
1520
|
}
|
|
1467
1521
|
if (instance.play) {
|
|
1468
1522
|
actions.push(
|
|
1523
|
+
{
|
|
1524
|
+
id: "replay-10",
|
|
1525
|
+
icon: "replay-10",
|
|
1526
|
+
label: "Rewind 10s",
|
|
1527
|
+
type: "button",
|
|
1528
|
+
group: "actions",
|
|
1529
|
+
execute: () => {
|
|
1530
|
+
instance.rewind?.(10);
|
|
1531
|
+
}
|
|
1532
|
+
},
|
|
1469
1533
|
{
|
|
1470
1534
|
id: "play",
|
|
1471
1535
|
icon: "play",
|
|
@@ -1485,6 +1549,26 @@ var MediaPlugin = class {
|
|
|
1485
1549
|
execute: () => {
|
|
1486
1550
|
instance.pause?.();
|
|
1487
1551
|
}
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
id: "forward-10",
|
|
1555
|
+
icon: "forward-10",
|
|
1556
|
+
label: "Fast Forward 10s",
|
|
1557
|
+
type: "button",
|
|
1558
|
+
group: "actions",
|
|
1559
|
+
execute: () => {
|
|
1560
|
+
instance.fastForward?.(10);
|
|
1561
|
+
}
|
|
1562
|
+
},
|
|
1563
|
+
{
|
|
1564
|
+
id: "speed",
|
|
1565
|
+
icon: "speed",
|
|
1566
|
+
label: "Playback Speed",
|
|
1567
|
+
type: "button",
|
|
1568
|
+
group: "actions",
|
|
1569
|
+
execute: () => {
|
|
1570
|
+
instance.cycleSpeed?.();
|
|
1571
|
+
}
|
|
1488
1572
|
}
|
|
1489
1573
|
);
|
|
1490
1574
|
}
|
|
@@ -1607,6 +1691,28 @@ var MediaPlugin = class {
|
|
|
1607
1691
|
play: mediaElement ? () => mediaElement?.play() : void 0,
|
|
1608
1692
|
pause: mediaElement ? () => mediaElement?.pause() : void 0,
|
|
1609
1693
|
isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
|
|
1694
|
+
fastForward: mediaElement ? (seconds = 10) => {
|
|
1695
|
+
if (mediaElement) {
|
|
1696
|
+
mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
|
|
1697
|
+
}
|
|
1698
|
+
} : void 0,
|
|
1699
|
+
rewind: mediaElement ? (seconds = 10) => {
|
|
1700
|
+
if (mediaElement) {
|
|
1701
|
+
mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
|
|
1702
|
+
}
|
|
1703
|
+
} : void 0,
|
|
1704
|
+
cycleSpeed: mediaElement ? () => {
|
|
1705
|
+
if (mediaElement) {
|
|
1706
|
+
const speeds = [1, 1.25, 1.5, 2, 0.5];
|
|
1707
|
+
const curIndex = speeds.indexOf(mediaElement.playbackRate);
|
|
1708
|
+
const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
|
|
1709
|
+
mediaElement.playbackRate = speeds[nextIndex];
|
|
1710
|
+
}
|
|
1711
|
+
} : void 0,
|
|
1712
|
+
setPlaybackRate: mediaElement ? (rate) => {
|
|
1713
|
+
if (mediaElement) mediaElement.playbackRate = rate;
|
|
1714
|
+
} : void 0,
|
|
1715
|
+
getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
|
|
1610
1716
|
download: () => {
|
|
1611
1717
|
const a = document.createElement("a");
|
|
1612
1718
|
a.href = url;
|
|
@@ -1636,7 +1742,10 @@ var DocxPlugin = class {
|
|
|
1636
1742
|
supports(file) {
|
|
1637
1743
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1638
1744
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1639
|
-
|
|
1745
|
+
if (ext) {
|
|
1746
|
+
return this.extensions.includes(ext);
|
|
1747
|
+
}
|
|
1748
|
+
return this.mimeTypes.includes(mime || "");
|
|
1640
1749
|
}
|
|
1641
1750
|
getToolbarActions(instance) {
|
|
1642
1751
|
return [
|
|
@@ -1688,16 +1797,35 @@ var DocxPlugin = class {
|
|
|
1688
1797
|
wrapper.style.transformOrigin = "top center";
|
|
1689
1798
|
wrapper.style.transition = "transform 0.2s ease";
|
|
1690
1799
|
wrapper.style.padding = "24px 16px";
|
|
1691
|
-
wrapper.style.maxWidth = "
|
|
1800
|
+
wrapper.style.maxWidth = "950px";
|
|
1692
1801
|
wrapper.style.margin = "0 auto";
|
|
1693
1802
|
wrapper.style.boxSizing = "border-box";
|
|
1694
1803
|
ctx.container.style.overflow = "auto";
|
|
1695
1804
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
1696
1805
|
ctx.container.appendChild(wrapper);
|
|
1806
|
+
const styleOverride = document.createElement("style");
|
|
1807
|
+
styleOverride.textContent = `
|
|
1808
|
+
.fp-docx-wrapper .docx-wrapper {
|
|
1809
|
+
background: transparent !important;
|
|
1810
|
+
padding: 0 !important;
|
|
1811
|
+
display: flex !important;
|
|
1812
|
+
flex-direction: column !important;
|
|
1813
|
+
align-items: center !important;
|
|
1814
|
+
box-sizing: border-box !important;
|
|
1815
|
+
}
|
|
1816
|
+
.fp-docx-wrapper section.docx {
|
|
1817
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
|
|
1818
|
+
border-radius: 4px !important;
|
|
1819
|
+
margin-bottom: 24px !important;
|
|
1820
|
+
background-color: #ffffff !important;
|
|
1821
|
+
}
|
|
1822
|
+
`;
|
|
1823
|
+
ctx.container.appendChild(styleOverride);
|
|
1697
1824
|
let scale = 1;
|
|
1698
1825
|
let renderedSuccessfully = false;
|
|
1826
|
+
const createdBlobUrls = [];
|
|
1699
1827
|
try {
|
|
1700
|
-
await docx__namespace.renderAsync(ctx.buffer, wrapper,
|
|
1828
|
+
await docx__namespace.renderAsync(ctx.buffer, wrapper, wrapper, {
|
|
1701
1829
|
inWrapper: true,
|
|
1702
1830
|
ignoreWidth: false,
|
|
1703
1831
|
ignoreHeight: false,
|
|
@@ -1706,19 +1834,25 @@ var DocxPlugin = class {
|
|
|
1706
1834
|
breakPages: true,
|
|
1707
1835
|
experimental: true
|
|
1708
1836
|
});
|
|
1837
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1838
|
+
ctx.container.appendChild(wrapper);
|
|
1839
|
+
}
|
|
1709
1840
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
1710
1841
|
renderedSuccessfully = true;
|
|
1711
1842
|
}
|
|
1712
1843
|
} catch (err) {
|
|
1713
|
-
console.warn("[DocxPlugin] docx-preview failed, triggering native
|
|
1844
|
+
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
1714
1845
|
}
|
|
1715
1846
|
if (!renderedSuccessfully) {
|
|
1716
1847
|
try {
|
|
1717
1848
|
wrapper.innerHTML = "";
|
|
1718
|
-
this.renderXmlFallback(ctx, wrapper);
|
|
1849
|
+
this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
|
|
1850
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1851
|
+
ctx.container.appendChild(wrapper);
|
|
1852
|
+
}
|
|
1719
1853
|
renderedSuccessfully = true;
|
|
1720
1854
|
} catch (fallbackErr) {
|
|
1721
|
-
console.error("[DocxPlugin]
|
|
1855
|
+
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
1722
1856
|
wrapper.innerHTML = `
|
|
1723
1857
|
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
1724
1858
|
<div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
|
|
@@ -1726,10 +1860,17 @@ var DocxPlugin = class {
|
|
|
1726
1860
|
<p style="color: #64748b; margin: 0;">Could not parse document content</p>
|
|
1727
1861
|
</div>
|
|
1728
1862
|
`;
|
|
1863
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1864
|
+
ctx.container.appendChild(wrapper);
|
|
1865
|
+
}
|
|
1729
1866
|
}
|
|
1730
1867
|
}
|
|
1731
1868
|
const cleanup = () => {
|
|
1869
|
+
for (const url of createdBlobUrls) {
|
|
1870
|
+
URL.revokeObjectURL(url);
|
|
1871
|
+
}
|
|
1732
1872
|
wrapper.remove();
|
|
1873
|
+
styleOverride.remove();
|
|
1733
1874
|
ctx.container.innerHTML = "";
|
|
1734
1875
|
};
|
|
1735
1876
|
ctx.signal.addEventListener("abort", cleanup);
|
|
@@ -1766,13 +1907,50 @@ var DocxPlugin = class {
|
|
|
1766
1907
|
}
|
|
1767
1908
|
};
|
|
1768
1909
|
}
|
|
1769
|
-
renderXmlFallback(ctx, wrapper) {
|
|
1910
|
+
renderXmlFallback(ctx, wrapper, createdBlobUrls) {
|
|
1911
|
+
const magic = new Uint8Array(ctx.buffer.slice(0, 8));
|
|
1912
|
+
if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
|
|
1913
|
+
this.renderBinaryDocFallback(ctx, wrapper);
|
|
1914
|
+
return;
|
|
1915
|
+
}
|
|
1770
1916
|
const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
|
|
1771
|
-
const
|
|
1917
|
+
const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
|
|
1918
|
+
const docXmlEntry = docKey ? unzipped[docKey] : void 0;
|
|
1772
1919
|
if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
|
|
1773
1920
|
const xmlStr = fflate.strFromU8(docXmlEntry);
|
|
1774
1921
|
const parser = new DOMParser();
|
|
1775
1922
|
const doc = parser.parseFromString(xmlStr, "application/xml");
|
|
1923
|
+
const imageMap = {};
|
|
1924
|
+
const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
|
|
1925
|
+
if (relsKey && unzipped[relsKey]) {
|
|
1926
|
+
try {
|
|
1927
|
+
const relsXml = fflate.strFromU8(unzipped[relsKey]);
|
|
1928
|
+
const relsDoc = parser.parseFromString(relsXml, "application/xml");
|
|
1929
|
+
const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
|
|
1930
|
+
for (const rel of relEls) {
|
|
1931
|
+
const rId = rel.getAttribute("Id");
|
|
1932
|
+
const target = rel.getAttribute("Target");
|
|
1933
|
+
if (rId && target) {
|
|
1934
|
+
const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
|
|
1935
|
+
const fullTargetKey = Object.keys(unzipped).find((k) => {
|
|
1936
|
+
const norm = k.replace(/^[./\\]+/, "").toLowerCase();
|
|
1937
|
+
return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
|
|
1938
|
+
});
|
|
1939
|
+
if (fullTargetKey && unzipped[fullTargetKey]) {
|
|
1940
|
+
const bytes = unzipped[fullTargetKey];
|
|
1941
|
+
const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
|
|
1942
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
|
|
1943
|
+
const blob = new Blob([bytes], { type: mime });
|
|
1944
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1945
|
+
imageMap[rId] = blobUrl;
|
|
1946
|
+
createdBlobUrls.push(blobUrl);
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
} catch (relsErr) {
|
|
1951
|
+
console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1776
1954
|
const card = document.createElement("div");
|
|
1777
1955
|
card.className = "fp-docx-page-card";
|
|
1778
1956
|
card.style.backgroundColor = "#ffffff";
|
|
@@ -1790,7 +1968,7 @@ var DocxPlugin = class {
|
|
|
1790
1968
|
const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
|
|
1791
1969
|
const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
|
|
1792
1970
|
const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
|
|
1793
|
-
const textContent = this.extractParagraphHtml(child);
|
|
1971
|
+
const textContent = this.extractParagraphHtml(child, imageMap);
|
|
1794
1972
|
if (!textContent.trim()) {
|
|
1795
1973
|
html += '<div style="height: 10px;"></div>';
|
|
1796
1974
|
continue;
|
|
@@ -1816,7 +1994,7 @@ var DocxPlugin = class {
|
|
|
1816
1994
|
html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
1817
1995
|
const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
|
|
1818
1996
|
cells.forEach((tc) => {
|
|
1819
|
-
const cellText = this.extractParagraphHtml(tc);
|
|
1997
|
+
const cellText = this.extractParagraphHtml(tc, imageMap);
|
|
1820
1998
|
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || " "}</td>`;
|
|
1821
1999
|
});
|
|
1822
2000
|
html += "</tr>";
|
|
@@ -1824,34 +2002,63 @@ var DocxPlugin = class {
|
|
|
1824
2002
|
html += "</table>";
|
|
1825
2003
|
}
|
|
1826
2004
|
}
|
|
1827
|
-
card.innerHTML =
|
|
1828
|
-
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
|
|
2005
|
+
card.innerHTML = DOMPurify2__default.default.sanitize(html, {
|
|
2006
|
+
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
|
|
1829
2007
|
ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
|
|
1830
2008
|
});
|
|
1831
2009
|
wrapper.appendChild(card);
|
|
1832
2010
|
}
|
|
1833
|
-
extractParagraphHtml(pElement) {
|
|
2011
|
+
extractParagraphHtml(pElement, imageMap = {}) {
|
|
1834
2012
|
let result = "";
|
|
2013
|
+
const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
|
|
2014
|
+
for (const drawing of drawings) {
|
|
2015
|
+
const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
|
|
2016
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
2017
|
+
if (rId && imageMap[rId]) {
|
|
2018
|
+
result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
1835
2021
|
const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
|
|
1836
|
-
if (runs.length === 0) {
|
|
1837
|
-
return
|
|
2022
|
+
if (runs.length === 0 && !result) {
|
|
2023
|
+
return DOMPurify2__default.default.sanitize(pElement.textContent || "");
|
|
1838
2024
|
}
|
|
1839
2025
|
for (const r of runs) {
|
|
2026
|
+
const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
|
|
2027
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
2028
|
+
if (rId && imageMap[rId]) {
|
|
2029
|
+
result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
|
|
2030
|
+
}
|
|
2031
|
+
const brs = r.getElementsByTagNameNS("*", "br");
|
|
2032
|
+
for (let i = 0; i < brs.length; i++) {
|
|
2033
|
+
result += "<br/>";
|
|
2034
|
+
}
|
|
2035
|
+
const tabs = r.getElementsByTagNameNS("*", "tab");
|
|
2036
|
+
if (tabs.length > 0) {
|
|
2037
|
+
result += " ";
|
|
2038
|
+
}
|
|
1840
2039
|
const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
|
|
1841
2040
|
const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
|
|
1842
2041
|
const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
|
|
1843
2042
|
const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
|
|
2043
|
+
const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
|
|
1844
2044
|
const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
|
|
1845
2045
|
const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
|
|
2046
|
+
const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
|
|
2047
|
+
const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
|
|
1846
2048
|
const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
|
|
1847
2049
|
let text = texts.map((t) => t.textContent || "").join("");
|
|
1848
2050
|
if (!text) continue;
|
|
1849
|
-
text =
|
|
2051
|
+
text = DOMPurify2__default.default.sanitize(text);
|
|
1850
2052
|
let styles = "";
|
|
1851
2053
|
if (isBold) styles += "font-weight: bold; ";
|
|
1852
2054
|
if (isItalic) styles += "font-style: italic; ";
|
|
1853
2055
|
if (isUnderline) styles += "text-decoration: underline; ";
|
|
2056
|
+
if (isStrike) styles += "text-decoration: line-through; ";
|
|
1854
2057
|
if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
|
|
2058
|
+
if (szVal) {
|
|
2059
|
+
const pt = parseInt(szVal, 10) / 2;
|
|
2060
|
+
if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
|
|
2061
|
+
}
|
|
1855
2062
|
if (styles) {
|
|
1856
2063
|
result += `<span style="${styles}">${text}</span>`;
|
|
1857
2064
|
} else {
|
|
@@ -1860,6 +2067,52 @@ var DocxPlugin = class {
|
|
|
1860
2067
|
}
|
|
1861
2068
|
return result;
|
|
1862
2069
|
}
|
|
2070
|
+
renderBinaryDocFallback(ctx, wrapper) {
|
|
2071
|
+
const card = document.createElement("div");
|
|
2072
|
+
card.className = "fp-docx-page-card";
|
|
2073
|
+
card.style.backgroundColor = "#ffffff";
|
|
2074
|
+
card.style.borderRadius = "6px";
|
|
2075
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
2076
|
+
card.style.padding = "48px 56px";
|
|
2077
|
+
card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
2078
|
+
card.style.color = "#1e293b";
|
|
2079
|
+
card.style.lineHeight = "1.6";
|
|
2080
|
+
try {
|
|
2081
|
+
const cfbf = new CfbfReader(ctx.buffer);
|
|
2082
|
+
const wordDocStream = cfbf.readStream("WordDocument");
|
|
2083
|
+
if (wordDocStream && wordDocStream.length >= 512) {
|
|
2084
|
+
const text2 = this.extractReadableStrings(wordDocStream);
|
|
2085
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text2)}</div>`;
|
|
2086
|
+
wrapper.appendChild(card);
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
} catch {
|
|
2090
|
+
}
|
|
2091
|
+
const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
|
|
2092
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text)}</div>`;
|
|
2093
|
+
wrapper.appendChild(card);
|
|
2094
|
+
}
|
|
2095
|
+
extractReadableStrings(bytes) {
|
|
2096
|
+
let result = "";
|
|
2097
|
+
let current = "";
|
|
2098
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2099
|
+
const b = bytes[i];
|
|
2100
|
+
if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
|
|
2101
|
+
current += String.fromCharCode(b);
|
|
2102
|
+
} else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
|
|
2103
|
+
continue;
|
|
2104
|
+
} else {
|
|
2105
|
+
if (current.trim().length > 3) {
|
|
2106
|
+
result += current.trim() + "\n\n";
|
|
2107
|
+
}
|
|
2108
|
+
current = "";
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
if (current.trim().length > 3) {
|
|
2112
|
+
result += current.trim();
|
|
2113
|
+
}
|
|
2114
|
+
return result;
|
|
2115
|
+
}
|
|
1863
2116
|
};
|
|
1864
2117
|
function docxPlugin() {
|
|
1865
2118
|
return new DocxPlugin();
|
|
@@ -1881,7 +2134,10 @@ var ExcelPlugin = class {
|
|
|
1881
2134
|
supports(file) {
|
|
1882
2135
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1883
2136
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1884
|
-
|
|
2137
|
+
if (ext) {
|
|
2138
|
+
return this.extensions.includes(ext);
|
|
2139
|
+
}
|
|
2140
|
+
return this.mimeTypes.includes(mime || "");
|
|
1885
2141
|
}
|
|
1886
2142
|
getToolbarActions(instance) {
|
|
1887
2143
|
return [
|
|
@@ -2671,7 +2927,7 @@ var MarkdownPlugin = class {
|
|
|
2671
2927
|
gfm: true,
|
|
2672
2928
|
breaks: true
|
|
2673
2929
|
});
|
|
2674
|
-
const cleanHtml =
|
|
2930
|
+
const cleanHtml = DOMPurify2__default.default.sanitize(rawHtml, {
|
|
2675
2931
|
USE_PROFILES: { html: true }
|
|
2676
2932
|
});
|
|
2677
2933
|
const wrapper = document.createElement("div");
|
|
@@ -2823,7 +3079,10 @@ var PptxPlugin = class {
|
|
|
2823
3079
|
supports(file) {
|
|
2824
3080
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2825
3081
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2826
|
-
|
|
3082
|
+
if (ext) {
|
|
3083
|
+
return this.extensions.includes(ext);
|
|
3084
|
+
}
|
|
3085
|
+
return this.mimeTypes.includes(mime || "");
|
|
2827
3086
|
}
|
|
2828
3087
|
getToolbarActions(instance) {
|
|
2829
3088
|
return [
|
|
@@ -3268,7 +3527,10 @@ var RtfPlugin = class {
|
|
|
3268
3527
|
ctx.container.appendChild(wrapper);
|
|
3269
3528
|
let scale = 1;
|
|
3270
3529
|
try {
|
|
3271
|
-
|
|
3530
|
+
if (typeof RTFJS__namespace.loggingEnabled === "function") {
|
|
3531
|
+
RTFJS__namespace.loggingEnabled(false);
|
|
3532
|
+
}
|
|
3533
|
+
const doc = new RTFJS__namespace.Document(ctx.buffer, {});
|
|
3272
3534
|
const htmlElements = await doc.render();
|
|
3273
3535
|
for (const el of htmlElements) {
|
|
3274
3536
|
wrapper.appendChild(el);
|
|
@@ -3386,7 +3648,7 @@ var HtmlPreviewPlugin = class {
|
|
|
3386
3648
|
}
|
|
3387
3649
|
async render(ctx) {
|
|
3388
3650
|
const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
|
|
3389
|
-
const sanitized =
|
|
3651
|
+
const sanitized = DOMPurify2__default.default.sanitize(rawHtml, {
|
|
3390
3652
|
WHOLE_DOCUMENT: true,
|
|
3391
3653
|
ADD_TAGS: ["style", "link"],
|
|
3392
3654
|
ADD_ATTR: ["target", "rel"]
|
|
@@ -3466,7 +3728,10 @@ var OpenDocumentPlugin = class {
|
|
|
3466
3728
|
supports(file) {
|
|
3467
3729
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3468
3730
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3469
|
-
|
|
3731
|
+
if (ext) {
|
|
3732
|
+
return this.extensions.includes(ext);
|
|
3733
|
+
}
|
|
3734
|
+
return this.mimeTypes.includes(mime || "");
|
|
3470
3735
|
}
|
|
3471
3736
|
getToolbarActions(instance) {
|
|
3472
3737
|
const isPresentation = instance.isPresentation;
|
|
@@ -3629,7 +3894,7 @@ var OpenDocumentPlugin = class {
|
|
|
3629
3894
|
wrapper.style.padding = "48px";
|
|
3630
3895
|
wrapper.style.minHeight = "100%";
|
|
3631
3896
|
const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
|
|
3632
|
-
wrapper.innerHTML =
|
|
3897
|
+
wrapper.innerHTML = DOMPurify2__default.default.sanitize(bodyHtml, {
|
|
3633
3898
|
ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
|
|
3634
3899
|
ADD_ATTR: ["style", "colspan", "rowspan"]
|
|
3635
3900
|
});
|
|
@@ -3881,7 +4146,10 @@ var DocPlugin = class {
|
|
|
3881
4146
|
supports(file) {
|
|
3882
4147
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3883
4148
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3884
|
-
|
|
4149
|
+
if (ext) {
|
|
4150
|
+
return this.extensions.includes(ext);
|
|
4151
|
+
}
|
|
4152
|
+
return this.mimeTypes.includes(mime || "");
|
|
3885
4153
|
}
|
|
3886
4154
|
getToolbarActions(instance) {
|
|
3887
4155
|
return [
|
|
@@ -4104,22 +4372,22 @@ var DocPlugin = class {
|
|
|
4104
4372
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4105
4373
|
*/
|
|
4106
4374
|
extractStringsFromBytes(bytes) {
|
|
4107
|
-
const
|
|
4108
|
-
const
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4375
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4376
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4377
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4378
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4379
|
+
const candidateLines = [];
|
|
4380
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4381
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4382
|
+
const trimmed = run.trim();
|
|
4383
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4384
|
+
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)) {
|
|
4385
|
+
seen.add(trimmed);
|
|
4386
|
+
candidateLines.push(trimmed);
|
|
4387
|
+
}
|
|
4120
4388
|
}
|
|
4121
4389
|
}
|
|
4122
|
-
return
|
|
4390
|
+
return candidateLines.join("\n\n");
|
|
4123
4391
|
}
|
|
4124
4392
|
heuristicTextExtraction(buffer) {
|
|
4125
4393
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4149,24 +4417,24 @@ var DocPlugin = class {
|
|
|
4149
4417
|
html += "</ul>";
|
|
4150
4418
|
inList = false;
|
|
4151
4419
|
}
|
|
4152
|
-
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${
|
|
4420
|
+
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>`;
|
|
4153
4421
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
4154
4422
|
if (!inList) {
|
|
4155
4423
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
4156
4424
|
inList = true;
|
|
4157
4425
|
}
|
|
4158
4426
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
4159
|
-
html += `<li style="margin: 4px 0; line-height: 1.6;">${
|
|
4427
|
+
html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2__default.default.sanitize(bulletText)}</li>`;
|
|
4160
4428
|
} else {
|
|
4161
4429
|
if (inList) {
|
|
4162
4430
|
html += "</ul>";
|
|
4163
4431
|
inList = false;
|
|
4164
4432
|
}
|
|
4165
|
-
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${
|
|
4433
|
+
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2__default.default.sanitize(line)}</p>`;
|
|
4166
4434
|
}
|
|
4167
4435
|
}
|
|
4168
4436
|
if (inList) html += "</ul>";
|
|
4169
|
-
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${
|
|
4437
|
+
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2__default.default.sanitize(filename)})</p>`;
|
|
4170
4438
|
}
|
|
4171
4439
|
};
|
|
4172
4440
|
function docPlugin() {
|
|
@@ -4174,14 +4442,17 @@ function docPlugin() {
|
|
|
4174
4442
|
}
|
|
4175
4443
|
var PptPlugin = class {
|
|
4176
4444
|
id = "ppt";
|
|
4177
|
-
name = "
|
|
4445
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4178
4446
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4179
4447
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4180
|
-
weight =
|
|
4448
|
+
weight = 85;
|
|
4181
4449
|
supports(file) {
|
|
4182
4450
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4183
4451
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4184
|
-
|
|
4452
|
+
if (ext) {
|
|
4453
|
+
return this.extensions.includes(ext);
|
|
4454
|
+
}
|
|
4455
|
+
return this.mimeTypes.includes(mime || "");
|
|
4185
4456
|
}
|
|
4186
4457
|
getToolbarActions(instance) {
|
|
4187
4458
|
return [
|
|
@@ -4269,19 +4540,15 @@ var PptPlugin = class {
|
|
|
4269
4540
|
container.style.alignItems = "center";
|
|
4270
4541
|
container.style.padding = "32px 16px";
|
|
4271
4542
|
container.style.backgroundColor = "#0f172a";
|
|
4543
|
+
container.style.boxSizing = "border-box";
|
|
4272
4544
|
const slideCard = document.createElement("div");
|
|
4273
4545
|
slideCard.className = "fp-ppt-slide-card";
|
|
4274
4546
|
slideCard.style.width = "960px";
|
|
4275
|
-
slideCard.style.maxWidth = "
|
|
4547
|
+
slideCard.style.maxWidth = "92%";
|
|
4276
4548
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4277
4549
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4278
|
-
slideCard.style.boxShadow = "0
|
|
4550
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4279
4551
|
slideCard.style.borderRadius = "8px";
|
|
4280
|
-
slideCard.style.padding = "48px";
|
|
4281
|
-
slideCard.style.display = "flex";
|
|
4282
|
-
slideCard.style.flexDirection = "column";
|
|
4283
|
-
slideCard.style.justifyContent = "center";
|
|
4284
|
-
slideCard.style.alignItems = "center";
|
|
4285
4552
|
slideCard.style.boxSizing = "border-box";
|
|
4286
4553
|
slideCard.style.position = "relative";
|
|
4287
4554
|
slideCard.style.overflow = "hidden";
|
|
@@ -4292,21 +4559,25 @@ var PptPlugin = class {
|
|
|
4292
4559
|
let scale = 1;
|
|
4293
4560
|
let currentSlide = 1;
|
|
4294
4561
|
let slides = [];
|
|
4562
|
+
const createdBlobUrls = [];
|
|
4295
4563
|
try {
|
|
4296
4564
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4297
4565
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4298
4566
|
if (!pptStream || pptStream.length < 512) {
|
|
4299
4567
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4300
4568
|
}
|
|
4301
|
-
|
|
4569
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4570
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4302
4571
|
} catch (err) {
|
|
4303
4572
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4304
4573
|
}
|
|
4305
4574
|
if (slides.length === 0) {
|
|
4306
4575
|
slides = [
|
|
4307
4576
|
{
|
|
4577
|
+
slideIndex: 1,
|
|
4308
4578
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4309
|
-
|
|
4579
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4580
|
+
tableColumns: []
|
|
4310
4581
|
}
|
|
4311
4582
|
];
|
|
4312
4583
|
}
|
|
@@ -4315,23 +4586,73 @@ var PptPlugin = class {
|
|
|
4315
4586
|
currentSlide = idx;
|
|
4316
4587
|
const s = slides[idx - 1];
|
|
4317
4588
|
if (!s) return;
|
|
4589
|
+
let contentHtml = "";
|
|
4590
|
+
if (s.pictureUrl) {
|
|
4591
|
+
contentHtml = `
|
|
4592
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4593
|
+
<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;" />
|
|
4594
|
+
</div>
|
|
4595
|
+
`;
|
|
4596
|
+
} else if (s.tableColumns.length > 0) {
|
|
4597
|
+
const cols = s.tableColumns;
|
|
4598
|
+
contentHtml = `
|
|
4599
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4600
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4601
|
+
<thead>
|
|
4602
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4603
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2__default.default.sanitize(c)}</th>`).join("")}
|
|
4604
|
+
</tr>
|
|
4605
|
+
</thead>
|
|
4606
|
+
<tbody>
|
|
4607
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4608
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4609
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4610
|
+
</tr>
|
|
4611
|
+
`).join("")}
|
|
4612
|
+
</tbody>
|
|
4613
|
+
</table>
|
|
4614
|
+
</div>
|
|
4615
|
+
`;
|
|
4616
|
+
} else {
|
|
4617
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4618
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4619
|
+
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("");
|
|
4620
|
+
}).join("");
|
|
4621
|
+
contentHtml = `
|
|
4622
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4623
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4624
|
+
</div>
|
|
4625
|
+
`;
|
|
4626
|
+
}
|
|
4318
4627
|
slideCard.innerHTML = `
|
|
4319
|
-
<div style="
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4628
|
+
<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;">
|
|
4629
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4630
|
+
<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;">
|
|
4631
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4632
|
+
${DOMPurify2__default.default.sanitize(s.title)}
|
|
4633
|
+
</h1>
|
|
4634
|
+
${s.subtitle ? `
|
|
4635
|
+
<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);">
|
|
4636
|
+
${DOMPurify2__default.default.sanitize(s.subtitle)}
|
|
4637
|
+
</span>
|
|
4638
|
+
` : `
|
|
4639
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4640
|
+
Slide ${idx} / ${totalSlides}
|
|
4641
|
+
</span>
|
|
4642
|
+
`}
|
|
4328
4643
|
</div>
|
|
4644
|
+
|
|
4645
|
+
<!-- Slide Content -->
|
|
4646
|
+
${contentHtml}
|
|
4329
4647
|
</div>
|
|
4330
4648
|
`;
|
|
4331
4649
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4332
4650
|
};
|
|
4333
4651
|
renderSlide(1);
|
|
4334
4652
|
const cleanup = () => {
|
|
4653
|
+
for (const u of createdBlobUrls) {
|
|
4654
|
+
URL.revokeObjectURL(u);
|
|
4655
|
+
}
|
|
4335
4656
|
container.remove();
|
|
4336
4657
|
ctx.container.innerHTML = "";
|
|
4337
4658
|
};
|
|
@@ -4371,13 +4692,48 @@ var PptPlugin = class {
|
|
|
4371
4692
|
if (!ctx2d) return;
|
|
4372
4693
|
canvas.width = 160;
|
|
4373
4694
|
canvas.height = 90;
|
|
4374
|
-
ctx2d.fillStyle = "#
|
|
4695
|
+
ctx2d.fillStyle = "#334155";
|
|
4375
4696
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4376
|
-
ctx2d.fillStyle = "#
|
|
4377
|
-
ctx2d.
|
|
4378
|
-
ctx2d.
|
|
4379
|
-
|
|
4380
|
-
ctx2d.
|
|
4697
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4698
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4699
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4700
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4701
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4702
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4703
|
+
ctx2d.textAlign = "left";
|
|
4704
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4705
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4706
|
+
if (s.subtitle) {
|
|
4707
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4708
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4709
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4710
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4711
|
+
ctx2d.textAlign = "center";
|
|
4712
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4713
|
+
}
|
|
4714
|
+
if (s.pictureUrl) {
|
|
4715
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4716
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4717
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4718
|
+
ctx2d.font = "8px sans-serif";
|
|
4719
|
+
ctx2d.textAlign = "center";
|
|
4720
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4721
|
+
} else if (s.tableColumns.length > 0) {
|
|
4722
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4723
|
+
ctx2d.lineWidth = 1;
|
|
4724
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4725
|
+
for (let l = 1; l <= 3; l++) {
|
|
4726
|
+
ctx2d.beginPath();
|
|
4727
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4728
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4729
|
+
ctx2d.stroke();
|
|
4730
|
+
}
|
|
4731
|
+
} else {
|
|
4732
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4733
|
+
for (let l = 0; l < 4; l++) {
|
|
4734
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4735
|
+
}
|
|
4736
|
+
}
|
|
4381
4737
|
}
|
|
4382
4738
|
}));
|
|
4383
4739
|
},
|
|
@@ -4395,66 +4751,165 @@ var PptPlugin = class {
|
|
|
4395
4751
|
}
|
|
4396
4752
|
};
|
|
4397
4753
|
}
|
|
4754
|
+
/**
|
|
4755
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4756
|
+
*/
|
|
4757
|
+
extractPictures(cfbf, createdUrls) {
|
|
4758
|
+
const urls = [];
|
|
4759
|
+
try {
|
|
4760
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4761
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4762
|
+
const pBuf = new Uint8Array(picStream);
|
|
4763
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4764
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4765
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4766
|
+
let match = true;
|
|
4767
|
+
for (let j = 0; j < 8; j++) {
|
|
4768
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4769
|
+
match = false;
|
|
4770
|
+
break;
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
if (match) {
|
|
4774
|
+
let endIdx = -1;
|
|
4775
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4776
|
+
let endMatch = true;
|
|
4777
|
+
for (let j = 0; j < 8; j++) {
|
|
4778
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4779
|
+
endMatch = false;
|
|
4780
|
+
break;
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
if (endMatch) {
|
|
4784
|
+
endIdx = k + 8;
|
|
4785
|
+
break;
|
|
4786
|
+
}
|
|
4787
|
+
}
|
|
4788
|
+
if (endIdx !== -1) {
|
|
4789
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4790
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4791
|
+
const url = URL.createObjectURL(blob);
|
|
4792
|
+
urls.push(url);
|
|
4793
|
+
createdUrls.push(url);
|
|
4794
|
+
i = endIdx;
|
|
4795
|
+
}
|
|
4796
|
+
}
|
|
4797
|
+
}
|
|
4798
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4799
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4800
|
+
let endIdx = -1;
|
|
4801
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4802
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4803
|
+
endIdx = k + 2;
|
|
4804
|
+
break;
|
|
4805
|
+
}
|
|
4806
|
+
}
|
|
4807
|
+
if (endIdx !== -1) {
|
|
4808
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4809
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4810
|
+
const url = URL.createObjectURL(blob);
|
|
4811
|
+
urls.push(url);
|
|
4812
|
+
createdUrls.push(url);
|
|
4813
|
+
i = endIdx;
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
} catch (err) {
|
|
4818
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4819
|
+
}
|
|
4820
|
+
return urls;
|
|
4821
|
+
}
|
|
4398
4822
|
/**
|
|
4399
4823
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4400
4824
|
*/
|
|
4401
|
-
extractSlides(stream) {
|
|
4825
|
+
extractSlides(stream, pictures) {
|
|
4402
4826
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4403
4827
|
const len = stream.length;
|
|
4404
4828
|
let offset = 0;
|
|
4405
4829
|
const slides = [];
|
|
4406
|
-
let
|
|
4830
|
+
let picIdx = 0;
|
|
4407
4831
|
while (offset + 8 <= len) {
|
|
4408
4832
|
const recVerInst = view.getUint16(offset, true);
|
|
4409
4833
|
const recType = view.getUint16(offset + 2, true);
|
|
4410
4834
|
const recLen = view.getUint32(offset + 4, true);
|
|
4835
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4411
4836
|
if (recType === 1006) {
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4837
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4838
|
+
const rawTexts = [];
|
|
4839
|
+
let hasOle = false;
|
|
4840
|
+
let sOff = offset + 8;
|
|
4841
|
+
while (sOff + 8 <= slideEnd) {
|
|
4842
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4843
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4844
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4845
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4846
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4847
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4848
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4849
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4850
|
+
rawTexts.push(txt);
|
|
4851
|
+
}
|
|
4852
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4853
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4854
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4855
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4856
|
+
rawTexts.push(txt);
|
|
4857
|
+
}
|
|
4858
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4859
|
+
hasOle = true;
|
|
4860
|
+
}
|
|
4861
|
+
if (cIsContainer) sOff += 8;
|
|
4862
|
+
else sOff += 8 + cLen;
|
|
4417
4863
|
}
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4864
|
+
let title = "";
|
|
4865
|
+
let subtitle = "";
|
|
4866
|
+
const paragraphs = [];
|
|
4867
|
+
const tableColumns = [];
|
|
4868
|
+
for (const t of rawTexts) {
|
|
4869
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4870
|
+
title = t;
|
|
4871
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4872
|
+
tableColumns.push(t);
|
|
4873
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4874
|
+
subtitle = t;
|
|
4875
|
+
} else {
|
|
4876
|
+
paragraphs.push(t);
|
|
4877
|
+
}
|
|
4426
4878
|
}
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4431
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4432
|
-
currentSlideTexts.push(text);
|
|
4879
|
+
let pictureUrl = null;
|
|
4880
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4881
|
+
pictureUrl = pictures[picIdx++];
|
|
4433
4882
|
}
|
|
4883
|
+
slides.push({
|
|
4884
|
+
slideIndex: slides.length + 1,
|
|
4885
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4886
|
+
subtitle,
|
|
4887
|
+
paragraphs,
|
|
4888
|
+
tableColumns,
|
|
4889
|
+
pictureUrl,
|
|
4890
|
+
hasOle
|
|
4891
|
+
});
|
|
4434
4892
|
}
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
offset += 8;
|
|
4438
|
-
} else {
|
|
4439
|
-
offset += 8 + recLen;
|
|
4440
|
-
}
|
|
4441
|
-
}
|
|
4442
|
-
if (currentSlideTexts.length > 0) {
|
|
4443
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4444
|
-
const texts = currentSlideTexts.slice(1);
|
|
4445
|
-
slides.push({ title, texts });
|
|
4893
|
+
if (isContainer) offset += 8;
|
|
4894
|
+
else offset += 8 + recLen;
|
|
4446
4895
|
}
|
|
4447
4896
|
if (slides.length === 0) {
|
|
4448
4897
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4449
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4450
|
-
const
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4898
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4899
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4900
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4901
|
+
);
|
|
4902
|
+
if (clean.length > 0) {
|
|
4903
|
+
const chunkSize = 3;
|
|
4904
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4905
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4455
4906
|
slides.push({
|
|
4907
|
+
slideIndex: slides.length + 1,
|
|
4456
4908
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4457
|
-
|
|
4909
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4910
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4911
|
+
tableColumns: [],
|
|
4912
|
+
pictureUrl: pictures[slides.length] || null
|
|
4458
4913
|
});
|
|
4459
4914
|
}
|
|
4460
4915
|
}
|