@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.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Component, ChangeDetectionStrategy, Input, Output, ViewChild, EventEmitter as EventEmitter$1 } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
|
-
import
|
|
3
|
+
import DOMPurify2 from 'dompurify';
|
|
4
4
|
import * as docx from 'docx-preview';
|
|
5
5
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
6
6
|
import * as XLSX from 'xlsx';
|
|
@@ -11,7 +11,7 @@ import * as THREE from 'three';
|
|
|
11
11
|
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
|
|
12
12
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
13
13
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
14
|
-
import
|
|
14
|
+
import * as RTFJS from 'rtf.js/dist/RTFJS.bundle.js';
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -314,6 +314,39 @@ function detectOoxmlType(buffer) {
|
|
|
314
314
|
}
|
|
315
315
|
return "application/zip";
|
|
316
316
|
}
|
|
317
|
+
function detectCfbfType(buffer) {
|
|
318
|
+
const bytes = new Uint8Array(buffer);
|
|
319
|
+
const hasUtf16le = (str) => {
|
|
320
|
+
const target = new Uint8Array(str.length * 2);
|
|
321
|
+
for (let i = 0; i < str.length; i++) {
|
|
322
|
+
target[i * 2] = str.charCodeAt(i);
|
|
323
|
+
target[i * 2 + 1] = 0;
|
|
324
|
+
}
|
|
325
|
+
const targetLen = target.length;
|
|
326
|
+
const max = bytes.length - targetLen;
|
|
327
|
+
for (let i = 0; i <= max; i++) {
|
|
328
|
+
let match = true;
|
|
329
|
+
for (let j = 0; j < targetLen; j++) {
|
|
330
|
+
if (bytes[i + j] !== target[j]) {
|
|
331
|
+
match = false;
|
|
332
|
+
break;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (match) return true;
|
|
336
|
+
}
|
|
337
|
+
return false;
|
|
338
|
+
};
|
|
339
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
340
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
341
|
+
}
|
|
342
|
+
if (hasUtf16le("WordDocument")) {
|
|
343
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
344
|
+
}
|
|
345
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
346
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
317
350
|
function extractExtension(nameOrUrl) {
|
|
318
351
|
try {
|
|
319
352
|
const url = new URL(nameOrUrl);
|
|
@@ -378,6 +411,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
378
411
|
} else {
|
|
379
412
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
380
413
|
}
|
|
414
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
415
|
+
if (metadata.extension) {
|
|
416
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
417
|
+
} else {
|
|
418
|
+
const cfbf = detectCfbfType(buffer);
|
|
419
|
+
if (cfbf) {
|
|
420
|
+
metadata.mimeType = cfbf.mime;
|
|
421
|
+
metadata.extension = cfbf.extension;
|
|
422
|
+
} else {
|
|
423
|
+
metadata.mimeType = magicMime;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
381
426
|
} else {
|
|
382
427
|
metadata.mimeType = magicMime;
|
|
383
428
|
}
|
|
@@ -388,7 +433,7 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
388
433
|
return { buffer, metadata };
|
|
389
434
|
}
|
|
390
435
|
function sanitizeSVG(svg) {
|
|
391
|
-
return
|
|
436
|
+
return DOMPurify2.sanitize(svg, {
|
|
392
437
|
USE_PROFILES: { svg: true, svgFilters: true }
|
|
393
438
|
});
|
|
394
439
|
}
|
|
@@ -448,6 +493,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
448
493
|
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>`;
|
|
449
494
|
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>`;
|
|
450
495
|
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>`;
|
|
496
|
+
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>`;
|
|
497
|
+
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>`;
|
|
498
|
+
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>`;
|
|
451
499
|
var ICON_MAP = {
|
|
452
500
|
"zoom-in": ICON_ZOOM_IN,
|
|
453
501
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -468,7 +516,12 @@ var ICON_MAP = {
|
|
|
468
516
|
"page-prev": ICON_PAGE_PREV,
|
|
469
517
|
"page-next": ICON_PAGE_NEXT,
|
|
470
518
|
"chevron-left": ICON_PAGE_PREV,
|
|
471
|
-
"chevron-right": ICON_PAGE_NEXT
|
|
519
|
+
"chevron-right": ICON_PAGE_NEXT,
|
|
520
|
+
"fast-forward": ICON_FAST_FORWARD,
|
|
521
|
+
"forward-10": ICON_FAST_FORWARD,
|
|
522
|
+
"rewind": ICON_REWIND,
|
|
523
|
+
"replay-10": ICON_REWIND,
|
|
524
|
+
"speed": ICON_SPEED
|
|
472
525
|
};
|
|
473
526
|
var ToolbarController = class {
|
|
474
527
|
el;
|
|
@@ -1436,6 +1489,16 @@ var MediaPlugin = class {
|
|
|
1436
1489
|
}
|
|
1437
1490
|
if (instance.play) {
|
|
1438
1491
|
actions.push(
|
|
1492
|
+
{
|
|
1493
|
+
id: "replay-10",
|
|
1494
|
+
icon: "replay-10",
|
|
1495
|
+
label: "Rewind 10s",
|
|
1496
|
+
type: "button",
|
|
1497
|
+
group: "actions",
|
|
1498
|
+
execute: () => {
|
|
1499
|
+
instance.rewind?.(10);
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1439
1502
|
{
|
|
1440
1503
|
id: "play",
|
|
1441
1504
|
icon: "play",
|
|
@@ -1455,6 +1518,26 @@ var MediaPlugin = class {
|
|
|
1455
1518
|
execute: () => {
|
|
1456
1519
|
instance.pause?.();
|
|
1457
1520
|
}
|
|
1521
|
+
},
|
|
1522
|
+
{
|
|
1523
|
+
id: "forward-10",
|
|
1524
|
+
icon: "forward-10",
|
|
1525
|
+
label: "Fast Forward 10s",
|
|
1526
|
+
type: "button",
|
|
1527
|
+
group: "actions",
|
|
1528
|
+
execute: () => {
|
|
1529
|
+
instance.fastForward?.(10);
|
|
1530
|
+
}
|
|
1531
|
+
},
|
|
1532
|
+
{
|
|
1533
|
+
id: "speed",
|
|
1534
|
+
icon: "speed",
|
|
1535
|
+
label: "Playback Speed",
|
|
1536
|
+
type: "button",
|
|
1537
|
+
group: "actions",
|
|
1538
|
+
execute: () => {
|
|
1539
|
+
instance.cycleSpeed?.();
|
|
1540
|
+
}
|
|
1458
1541
|
}
|
|
1459
1542
|
);
|
|
1460
1543
|
}
|
|
@@ -1577,6 +1660,28 @@ var MediaPlugin = class {
|
|
|
1577
1660
|
play: mediaElement ? () => mediaElement?.play() : void 0,
|
|
1578
1661
|
pause: mediaElement ? () => mediaElement?.pause() : void 0,
|
|
1579
1662
|
isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
|
|
1663
|
+
fastForward: mediaElement ? (seconds = 10) => {
|
|
1664
|
+
if (mediaElement) {
|
|
1665
|
+
mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
|
|
1666
|
+
}
|
|
1667
|
+
} : void 0,
|
|
1668
|
+
rewind: mediaElement ? (seconds = 10) => {
|
|
1669
|
+
if (mediaElement) {
|
|
1670
|
+
mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
|
|
1671
|
+
}
|
|
1672
|
+
} : void 0,
|
|
1673
|
+
cycleSpeed: mediaElement ? () => {
|
|
1674
|
+
if (mediaElement) {
|
|
1675
|
+
const speeds = [1, 1.25, 1.5, 2, 0.5];
|
|
1676
|
+
const curIndex = speeds.indexOf(mediaElement.playbackRate);
|
|
1677
|
+
const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
|
|
1678
|
+
mediaElement.playbackRate = speeds[nextIndex];
|
|
1679
|
+
}
|
|
1680
|
+
} : void 0,
|
|
1681
|
+
setPlaybackRate: mediaElement ? (rate) => {
|
|
1682
|
+
if (mediaElement) mediaElement.playbackRate = rate;
|
|
1683
|
+
} : void 0,
|
|
1684
|
+
getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
|
|
1580
1685
|
download: () => {
|
|
1581
1686
|
const a = document.createElement("a");
|
|
1582
1687
|
a.href = url;
|
|
@@ -1606,7 +1711,10 @@ var DocxPlugin = class {
|
|
|
1606
1711
|
supports(file) {
|
|
1607
1712
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1608
1713
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1609
|
-
|
|
1714
|
+
if (ext) {
|
|
1715
|
+
return this.extensions.includes(ext);
|
|
1716
|
+
}
|
|
1717
|
+
return this.mimeTypes.includes(mime || "");
|
|
1610
1718
|
}
|
|
1611
1719
|
getToolbarActions(instance) {
|
|
1612
1720
|
return [
|
|
@@ -1658,16 +1766,35 @@ var DocxPlugin = class {
|
|
|
1658
1766
|
wrapper.style.transformOrigin = "top center";
|
|
1659
1767
|
wrapper.style.transition = "transform 0.2s ease";
|
|
1660
1768
|
wrapper.style.padding = "24px 16px";
|
|
1661
|
-
wrapper.style.maxWidth = "
|
|
1769
|
+
wrapper.style.maxWidth = "950px";
|
|
1662
1770
|
wrapper.style.margin = "0 auto";
|
|
1663
1771
|
wrapper.style.boxSizing = "border-box";
|
|
1664
1772
|
ctx.container.style.overflow = "auto";
|
|
1665
1773
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
1666
1774
|
ctx.container.appendChild(wrapper);
|
|
1775
|
+
const styleOverride = document.createElement("style");
|
|
1776
|
+
styleOverride.textContent = `
|
|
1777
|
+
.fp-docx-wrapper .docx-wrapper {
|
|
1778
|
+
background: transparent !important;
|
|
1779
|
+
padding: 0 !important;
|
|
1780
|
+
display: flex !important;
|
|
1781
|
+
flex-direction: column !important;
|
|
1782
|
+
align-items: center !important;
|
|
1783
|
+
box-sizing: border-box !important;
|
|
1784
|
+
}
|
|
1785
|
+
.fp-docx-wrapper section.docx {
|
|
1786
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
|
|
1787
|
+
border-radius: 4px !important;
|
|
1788
|
+
margin-bottom: 24px !important;
|
|
1789
|
+
background-color: #ffffff !important;
|
|
1790
|
+
}
|
|
1791
|
+
`;
|
|
1792
|
+
ctx.container.appendChild(styleOverride);
|
|
1667
1793
|
let scale = 1;
|
|
1668
1794
|
let renderedSuccessfully = false;
|
|
1795
|
+
const createdBlobUrls = [];
|
|
1669
1796
|
try {
|
|
1670
|
-
await docx.renderAsync(ctx.buffer, wrapper,
|
|
1797
|
+
await docx.renderAsync(ctx.buffer, wrapper, wrapper, {
|
|
1671
1798
|
inWrapper: true,
|
|
1672
1799
|
ignoreWidth: false,
|
|
1673
1800
|
ignoreHeight: false,
|
|
@@ -1676,19 +1803,25 @@ var DocxPlugin = class {
|
|
|
1676
1803
|
breakPages: true,
|
|
1677
1804
|
experimental: true
|
|
1678
1805
|
});
|
|
1806
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1807
|
+
ctx.container.appendChild(wrapper);
|
|
1808
|
+
}
|
|
1679
1809
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
1680
1810
|
renderedSuccessfully = true;
|
|
1681
1811
|
}
|
|
1682
1812
|
} catch (err) {
|
|
1683
|
-
console.warn("[DocxPlugin] docx-preview failed, triggering native
|
|
1813
|
+
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
1684
1814
|
}
|
|
1685
1815
|
if (!renderedSuccessfully) {
|
|
1686
1816
|
try {
|
|
1687
1817
|
wrapper.innerHTML = "";
|
|
1688
|
-
this.renderXmlFallback(ctx, wrapper);
|
|
1818
|
+
this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
|
|
1819
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1820
|
+
ctx.container.appendChild(wrapper);
|
|
1821
|
+
}
|
|
1689
1822
|
renderedSuccessfully = true;
|
|
1690
1823
|
} catch (fallbackErr) {
|
|
1691
|
-
console.error("[DocxPlugin]
|
|
1824
|
+
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
1692
1825
|
wrapper.innerHTML = `
|
|
1693
1826
|
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
1694
1827
|
<div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
|
|
@@ -1696,10 +1829,17 @@ var DocxPlugin = class {
|
|
|
1696
1829
|
<p style="color: #64748b; margin: 0;">Could not parse document content</p>
|
|
1697
1830
|
</div>
|
|
1698
1831
|
`;
|
|
1832
|
+
if (!ctx.container.contains(wrapper)) {
|
|
1833
|
+
ctx.container.appendChild(wrapper);
|
|
1834
|
+
}
|
|
1699
1835
|
}
|
|
1700
1836
|
}
|
|
1701
1837
|
const cleanup = () => {
|
|
1838
|
+
for (const url of createdBlobUrls) {
|
|
1839
|
+
URL.revokeObjectURL(url);
|
|
1840
|
+
}
|
|
1702
1841
|
wrapper.remove();
|
|
1842
|
+
styleOverride.remove();
|
|
1703
1843
|
ctx.container.innerHTML = "";
|
|
1704
1844
|
};
|
|
1705
1845
|
ctx.signal.addEventListener("abort", cleanup);
|
|
@@ -1736,13 +1876,50 @@ var DocxPlugin = class {
|
|
|
1736
1876
|
}
|
|
1737
1877
|
};
|
|
1738
1878
|
}
|
|
1739
|
-
renderXmlFallback(ctx, wrapper) {
|
|
1879
|
+
renderXmlFallback(ctx, wrapper, createdBlobUrls) {
|
|
1880
|
+
const magic = new Uint8Array(ctx.buffer.slice(0, 8));
|
|
1881
|
+
if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
|
|
1882
|
+
this.renderBinaryDocFallback(ctx, wrapper);
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1740
1885
|
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
1741
|
-
const
|
|
1886
|
+
const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
|
|
1887
|
+
const docXmlEntry = docKey ? unzipped[docKey] : void 0;
|
|
1742
1888
|
if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
|
|
1743
1889
|
const xmlStr = strFromU8(docXmlEntry);
|
|
1744
1890
|
const parser = new DOMParser();
|
|
1745
1891
|
const doc = parser.parseFromString(xmlStr, "application/xml");
|
|
1892
|
+
const imageMap = {};
|
|
1893
|
+
const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
|
|
1894
|
+
if (relsKey && unzipped[relsKey]) {
|
|
1895
|
+
try {
|
|
1896
|
+
const relsXml = strFromU8(unzipped[relsKey]);
|
|
1897
|
+
const relsDoc = parser.parseFromString(relsXml, "application/xml");
|
|
1898
|
+
const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
|
|
1899
|
+
for (const rel of relEls) {
|
|
1900
|
+
const rId = rel.getAttribute("Id");
|
|
1901
|
+
const target = rel.getAttribute("Target");
|
|
1902
|
+
if (rId && target) {
|
|
1903
|
+
const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
|
|
1904
|
+
const fullTargetKey = Object.keys(unzipped).find((k) => {
|
|
1905
|
+
const norm = k.replace(/^[./\\]+/, "").toLowerCase();
|
|
1906
|
+
return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
|
|
1907
|
+
});
|
|
1908
|
+
if (fullTargetKey && unzipped[fullTargetKey]) {
|
|
1909
|
+
const bytes = unzipped[fullTargetKey];
|
|
1910
|
+
const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
|
|
1911
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
|
|
1912
|
+
const blob = new Blob([bytes], { type: mime });
|
|
1913
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
1914
|
+
imageMap[rId] = blobUrl;
|
|
1915
|
+
createdBlobUrls.push(blobUrl);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
} catch (relsErr) {
|
|
1920
|
+
console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1746
1923
|
const card = document.createElement("div");
|
|
1747
1924
|
card.className = "fp-docx-page-card";
|
|
1748
1925
|
card.style.backgroundColor = "#ffffff";
|
|
@@ -1760,7 +1937,7 @@ var DocxPlugin = class {
|
|
|
1760
1937
|
const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
|
|
1761
1938
|
const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
|
|
1762
1939
|
const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
|
|
1763
|
-
const textContent = this.extractParagraphHtml(child);
|
|
1940
|
+
const textContent = this.extractParagraphHtml(child, imageMap);
|
|
1764
1941
|
if (!textContent.trim()) {
|
|
1765
1942
|
html += '<div style="height: 10px;"></div>';
|
|
1766
1943
|
continue;
|
|
@@ -1786,7 +1963,7 @@ var DocxPlugin = class {
|
|
|
1786
1963
|
html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
1787
1964
|
const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
|
|
1788
1965
|
cells.forEach((tc) => {
|
|
1789
|
-
const cellText = this.extractParagraphHtml(tc);
|
|
1966
|
+
const cellText = this.extractParagraphHtml(tc, imageMap);
|
|
1790
1967
|
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || " "}</td>`;
|
|
1791
1968
|
});
|
|
1792
1969
|
html += "</tr>";
|
|
@@ -1794,34 +1971,63 @@ var DocxPlugin = class {
|
|
|
1794
1971
|
html += "</table>";
|
|
1795
1972
|
}
|
|
1796
1973
|
}
|
|
1797
|
-
card.innerHTML =
|
|
1798
|
-
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
|
|
1974
|
+
card.innerHTML = DOMPurify2.sanitize(html, {
|
|
1975
|
+
ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
|
|
1799
1976
|
ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
|
|
1800
1977
|
});
|
|
1801
1978
|
wrapper.appendChild(card);
|
|
1802
1979
|
}
|
|
1803
|
-
extractParagraphHtml(pElement) {
|
|
1980
|
+
extractParagraphHtml(pElement, imageMap = {}) {
|
|
1804
1981
|
let result = "";
|
|
1982
|
+
const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
|
|
1983
|
+
for (const drawing of drawings) {
|
|
1984
|
+
const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
|
|
1985
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1986
|
+
if (rId && imageMap[rId]) {
|
|
1987
|
+
result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1805
1990
|
const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
|
|
1806
|
-
if (runs.length === 0) {
|
|
1807
|
-
return
|
|
1991
|
+
if (runs.length === 0 && !result) {
|
|
1992
|
+
return DOMPurify2.sanitize(pElement.textContent || "");
|
|
1808
1993
|
}
|
|
1809
1994
|
for (const r of runs) {
|
|
1995
|
+
const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
|
|
1996
|
+
const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
|
|
1997
|
+
if (rId && imageMap[rId]) {
|
|
1998
|
+
result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
|
|
1999
|
+
}
|
|
2000
|
+
const brs = r.getElementsByTagNameNS("*", "br");
|
|
2001
|
+
for (let i = 0; i < brs.length; i++) {
|
|
2002
|
+
result += "<br/>";
|
|
2003
|
+
}
|
|
2004
|
+
const tabs = r.getElementsByTagNameNS("*", "tab");
|
|
2005
|
+
if (tabs.length > 0) {
|
|
2006
|
+
result += " ";
|
|
2007
|
+
}
|
|
1810
2008
|
const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
|
|
1811
2009
|
const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
|
|
1812
2010
|
const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
|
|
1813
2011
|
const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
|
|
2012
|
+
const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
|
|
1814
2013
|
const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
|
|
1815
2014
|
const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
|
|
2015
|
+
const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
|
|
2016
|
+
const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
|
|
1816
2017
|
const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
|
|
1817
2018
|
let text = texts.map((t) => t.textContent || "").join("");
|
|
1818
2019
|
if (!text) continue;
|
|
1819
|
-
text =
|
|
2020
|
+
text = DOMPurify2.sanitize(text);
|
|
1820
2021
|
let styles = "";
|
|
1821
2022
|
if (isBold) styles += "font-weight: bold; ";
|
|
1822
2023
|
if (isItalic) styles += "font-style: italic; ";
|
|
1823
2024
|
if (isUnderline) styles += "text-decoration: underline; ";
|
|
2025
|
+
if (isStrike) styles += "text-decoration: line-through; ";
|
|
1824
2026
|
if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
|
|
2027
|
+
if (szVal) {
|
|
2028
|
+
const pt = parseInt(szVal, 10) / 2;
|
|
2029
|
+
if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
|
|
2030
|
+
}
|
|
1825
2031
|
if (styles) {
|
|
1826
2032
|
result += `<span style="${styles}">${text}</span>`;
|
|
1827
2033
|
} else {
|
|
@@ -1830,6 +2036,52 @@ var DocxPlugin = class {
|
|
|
1830
2036
|
}
|
|
1831
2037
|
return result;
|
|
1832
2038
|
}
|
|
2039
|
+
renderBinaryDocFallback(ctx, wrapper) {
|
|
2040
|
+
const card = document.createElement("div");
|
|
2041
|
+
card.className = "fp-docx-page-card";
|
|
2042
|
+
card.style.backgroundColor = "#ffffff";
|
|
2043
|
+
card.style.borderRadius = "6px";
|
|
2044
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
2045
|
+
card.style.padding = "48px 56px";
|
|
2046
|
+
card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
2047
|
+
card.style.color = "#1e293b";
|
|
2048
|
+
card.style.lineHeight = "1.6";
|
|
2049
|
+
try {
|
|
2050
|
+
const cfbf = new CfbfReader(ctx.buffer);
|
|
2051
|
+
const wordDocStream = cfbf.readStream("WordDocument");
|
|
2052
|
+
if (wordDocStream && wordDocStream.length >= 512) {
|
|
2053
|
+
const text2 = this.extractReadableStrings(wordDocStream);
|
|
2054
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
|
|
2055
|
+
wrapper.appendChild(card);
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
} catch {
|
|
2059
|
+
}
|
|
2060
|
+
const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
|
|
2061
|
+
card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
|
|
2062
|
+
wrapper.appendChild(card);
|
|
2063
|
+
}
|
|
2064
|
+
extractReadableStrings(bytes) {
|
|
2065
|
+
let result = "";
|
|
2066
|
+
let current = "";
|
|
2067
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2068
|
+
const b = bytes[i];
|
|
2069
|
+
if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
|
|
2070
|
+
current += String.fromCharCode(b);
|
|
2071
|
+
} else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
|
|
2072
|
+
continue;
|
|
2073
|
+
} else {
|
|
2074
|
+
if (current.trim().length > 3) {
|
|
2075
|
+
result += current.trim() + "\n\n";
|
|
2076
|
+
}
|
|
2077
|
+
current = "";
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (current.trim().length > 3) {
|
|
2081
|
+
result += current.trim();
|
|
2082
|
+
}
|
|
2083
|
+
return result;
|
|
2084
|
+
}
|
|
1833
2085
|
};
|
|
1834
2086
|
function docxPlugin() {
|
|
1835
2087
|
return new DocxPlugin();
|
|
@@ -1851,7 +2103,10 @@ var ExcelPlugin = class {
|
|
|
1851
2103
|
supports(file) {
|
|
1852
2104
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1853
2105
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1854
|
-
|
|
2106
|
+
if (ext) {
|
|
2107
|
+
return this.extensions.includes(ext);
|
|
2108
|
+
}
|
|
2109
|
+
return this.mimeTypes.includes(mime || "");
|
|
1855
2110
|
}
|
|
1856
2111
|
getToolbarActions(instance) {
|
|
1857
2112
|
return [
|
|
@@ -2641,7 +2896,7 @@ var MarkdownPlugin = class {
|
|
|
2641
2896
|
gfm: true,
|
|
2642
2897
|
breaks: true
|
|
2643
2898
|
});
|
|
2644
|
-
const cleanHtml =
|
|
2899
|
+
const cleanHtml = DOMPurify2.sanitize(rawHtml, {
|
|
2645
2900
|
USE_PROFILES: { html: true }
|
|
2646
2901
|
});
|
|
2647
2902
|
const wrapper = document.createElement("div");
|
|
@@ -2793,7 +3048,10 @@ var PptxPlugin = class {
|
|
|
2793
3048
|
supports(file) {
|
|
2794
3049
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2795
3050
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2796
|
-
|
|
3051
|
+
if (ext) {
|
|
3052
|
+
return this.extensions.includes(ext);
|
|
3053
|
+
}
|
|
3054
|
+
return this.mimeTypes.includes(mime || "");
|
|
2797
3055
|
}
|
|
2798
3056
|
getToolbarActions(instance) {
|
|
2799
3057
|
return [
|
|
@@ -3238,6 +3496,9 @@ var RtfPlugin = class {
|
|
|
3238
3496
|
ctx.container.appendChild(wrapper);
|
|
3239
3497
|
let scale = 1;
|
|
3240
3498
|
try {
|
|
3499
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3500
|
+
RTFJS.loggingEnabled(false);
|
|
3501
|
+
}
|
|
3241
3502
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3242
3503
|
const htmlElements = await doc.render();
|
|
3243
3504
|
for (const el of htmlElements) {
|
|
@@ -3356,7 +3617,7 @@ var HtmlPreviewPlugin = class {
|
|
|
3356
3617
|
}
|
|
3357
3618
|
async render(ctx) {
|
|
3358
3619
|
const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
|
|
3359
|
-
const sanitized =
|
|
3620
|
+
const sanitized = DOMPurify2.sanitize(rawHtml, {
|
|
3360
3621
|
WHOLE_DOCUMENT: true,
|
|
3361
3622
|
ADD_TAGS: ["style", "link"],
|
|
3362
3623
|
ADD_ATTR: ["target", "rel"]
|
|
@@ -3436,7 +3697,10 @@ var OpenDocumentPlugin = class {
|
|
|
3436
3697
|
supports(file) {
|
|
3437
3698
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3438
3699
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3439
|
-
|
|
3700
|
+
if (ext) {
|
|
3701
|
+
return this.extensions.includes(ext);
|
|
3702
|
+
}
|
|
3703
|
+
return this.mimeTypes.includes(mime || "");
|
|
3440
3704
|
}
|
|
3441
3705
|
getToolbarActions(instance) {
|
|
3442
3706
|
const isPresentation = instance.isPresentation;
|
|
@@ -3599,7 +3863,7 @@ var OpenDocumentPlugin = class {
|
|
|
3599
3863
|
wrapper.style.padding = "48px";
|
|
3600
3864
|
wrapper.style.minHeight = "100%";
|
|
3601
3865
|
const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
|
|
3602
|
-
wrapper.innerHTML =
|
|
3866
|
+
wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
|
|
3603
3867
|
ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
|
|
3604
3868
|
ADD_ATTR: ["style", "colspan", "rowspan"]
|
|
3605
3869
|
});
|
|
@@ -3851,7 +4115,10 @@ var DocPlugin = class {
|
|
|
3851
4115
|
supports(file) {
|
|
3852
4116
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3853
4117
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3854
|
-
|
|
4118
|
+
if (ext) {
|
|
4119
|
+
return this.extensions.includes(ext);
|
|
4120
|
+
}
|
|
4121
|
+
return this.mimeTypes.includes(mime || "");
|
|
3855
4122
|
}
|
|
3856
4123
|
getToolbarActions(instance) {
|
|
3857
4124
|
return [
|
|
@@ -4074,22 +4341,22 @@ var DocPlugin = class {
|
|
|
4074
4341
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4075
4342
|
*/
|
|
4076
4343
|
extractStringsFromBytes(bytes) {
|
|
4077
|
-
const
|
|
4078
|
-
const
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4344
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4345
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4346
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4347
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4348
|
+
const candidateLines = [];
|
|
4349
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4350
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4351
|
+
const trimmed = run.trim();
|
|
4352
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4353
|
+
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)) {
|
|
4354
|
+
seen.add(trimmed);
|
|
4355
|
+
candidateLines.push(trimmed);
|
|
4356
|
+
}
|
|
4090
4357
|
}
|
|
4091
4358
|
}
|
|
4092
|
-
return
|
|
4359
|
+
return candidateLines.join("\n\n");
|
|
4093
4360
|
}
|
|
4094
4361
|
heuristicTextExtraction(buffer) {
|
|
4095
4362
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
@@ -4119,24 +4386,24 @@ var DocPlugin = class {
|
|
|
4119
4386
|
html += "</ul>";
|
|
4120
4387
|
inList = false;
|
|
4121
4388
|
}
|
|
4122
|
-
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${
|
|
4389
|
+
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2.sanitize(line)}</h2>`;
|
|
4123
4390
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
4124
4391
|
if (!inList) {
|
|
4125
4392
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
4126
4393
|
inList = true;
|
|
4127
4394
|
}
|
|
4128
4395
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
4129
|
-
html += `<li style="margin: 4px 0; line-height: 1.6;">${
|
|
4396
|
+
html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
|
|
4130
4397
|
} else {
|
|
4131
4398
|
if (inList) {
|
|
4132
4399
|
html += "</ul>";
|
|
4133
4400
|
inList = false;
|
|
4134
4401
|
}
|
|
4135
|
-
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${
|
|
4402
|
+
html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
|
|
4136
4403
|
}
|
|
4137
4404
|
}
|
|
4138
4405
|
if (inList) html += "</ul>";
|
|
4139
|
-
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${
|
|
4406
|
+
return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
|
|
4140
4407
|
}
|
|
4141
4408
|
};
|
|
4142
4409
|
function docPlugin() {
|
|
@@ -4144,14 +4411,17 @@ function docPlugin() {
|
|
|
4144
4411
|
}
|
|
4145
4412
|
var PptPlugin = class {
|
|
4146
4413
|
id = "ppt";
|
|
4147
|
-
name = "
|
|
4414
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4148
4415
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4149
4416
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4150
|
-
weight =
|
|
4417
|
+
weight = 85;
|
|
4151
4418
|
supports(file) {
|
|
4152
4419
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4153
4420
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4154
|
-
|
|
4421
|
+
if (ext) {
|
|
4422
|
+
return this.extensions.includes(ext);
|
|
4423
|
+
}
|
|
4424
|
+
return this.mimeTypes.includes(mime || "");
|
|
4155
4425
|
}
|
|
4156
4426
|
getToolbarActions(instance) {
|
|
4157
4427
|
return [
|
|
@@ -4239,19 +4509,15 @@ var PptPlugin = class {
|
|
|
4239
4509
|
container.style.alignItems = "center";
|
|
4240
4510
|
container.style.padding = "32px 16px";
|
|
4241
4511
|
container.style.backgroundColor = "#0f172a";
|
|
4512
|
+
container.style.boxSizing = "border-box";
|
|
4242
4513
|
const slideCard = document.createElement("div");
|
|
4243
4514
|
slideCard.className = "fp-ppt-slide-card";
|
|
4244
4515
|
slideCard.style.width = "960px";
|
|
4245
|
-
slideCard.style.maxWidth = "
|
|
4516
|
+
slideCard.style.maxWidth = "92%";
|
|
4246
4517
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4247
4518
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4248
|
-
slideCard.style.boxShadow = "0
|
|
4519
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4249
4520
|
slideCard.style.borderRadius = "8px";
|
|
4250
|
-
slideCard.style.padding = "48px";
|
|
4251
|
-
slideCard.style.display = "flex";
|
|
4252
|
-
slideCard.style.flexDirection = "column";
|
|
4253
|
-
slideCard.style.justifyContent = "center";
|
|
4254
|
-
slideCard.style.alignItems = "center";
|
|
4255
4521
|
slideCard.style.boxSizing = "border-box";
|
|
4256
4522
|
slideCard.style.position = "relative";
|
|
4257
4523
|
slideCard.style.overflow = "hidden";
|
|
@@ -4262,21 +4528,25 @@ var PptPlugin = class {
|
|
|
4262
4528
|
let scale = 1;
|
|
4263
4529
|
let currentSlide = 1;
|
|
4264
4530
|
let slides = [];
|
|
4531
|
+
const createdBlobUrls = [];
|
|
4265
4532
|
try {
|
|
4266
4533
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4267
4534
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4268
4535
|
if (!pptStream || pptStream.length < 512) {
|
|
4269
4536
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4270
4537
|
}
|
|
4271
|
-
|
|
4538
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4539
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4272
4540
|
} catch (err) {
|
|
4273
4541
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4274
4542
|
}
|
|
4275
4543
|
if (slides.length === 0) {
|
|
4276
4544
|
slides = [
|
|
4277
4545
|
{
|
|
4546
|
+
slideIndex: 1,
|
|
4278
4547
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4279
|
-
|
|
4548
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4549
|
+
tableColumns: []
|
|
4280
4550
|
}
|
|
4281
4551
|
];
|
|
4282
4552
|
}
|
|
@@ -4285,23 +4555,73 @@ var PptPlugin = class {
|
|
|
4285
4555
|
currentSlide = idx;
|
|
4286
4556
|
const s = slides[idx - 1];
|
|
4287
4557
|
if (!s) return;
|
|
4558
|
+
let contentHtml = "";
|
|
4559
|
+
if (s.pictureUrl) {
|
|
4560
|
+
contentHtml = `
|
|
4561
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
4562
|
+
<img src="${s.pictureUrl}" alt="${DOMPurify2.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
|
|
4563
|
+
</div>
|
|
4564
|
+
`;
|
|
4565
|
+
} else if (s.tableColumns.length > 0) {
|
|
4566
|
+
const cols = s.tableColumns;
|
|
4567
|
+
contentHtml = `
|
|
4568
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
4569
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
4570
|
+
<thead>
|
|
4571
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
4572
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
4573
|
+
</tr>
|
|
4574
|
+
</thead>
|
|
4575
|
+
<tbody>
|
|
4576
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
4577
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
4578
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
4579
|
+
</tr>
|
|
4580
|
+
`).join("")}
|
|
4581
|
+
</tbody>
|
|
4582
|
+
</table>
|
|
4583
|
+
</div>
|
|
4584
|
+
`;
|
|
4585
|
+
} else {
|
|
4586
|
+
const pTags = s.paragraphs.map((p) => {
|
|
4587
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
4588
|
+
return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2.sanitize(l)}</p>`).join("");
|
|
4589
|
+
}).join("");
|
|
4590
|
+
contentHtml = `
|
|
4591
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
4592
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
4593
|
+
</div>
|
|
4594
|
+
`;
|
|
4595
|
+
}
|
|
4288
4596
|
slideCard.innerHTML = `
|
|
4289
|
-
<div style="
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4597
|
+
<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;">
|
|
4598
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
4599
|
+
<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;">
|
|
4600
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
4601
|
+
${DOMPurify2.sanitize(s.title)}
|
|
4602
|
+
</h1>
|
|
4603
|
+
${s.subtitle ? `
|
|
4604
|
+
<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);">
|
|
4605
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
4606
|
+
</span>
|
|
4607
|
+
` : `
|
|
4608
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
4609
|
+
Slide ${idx} / ${totalSlides}
|
|
4610
|
+
</span>
|
|
4611
|
+
`}
|
|
4298
4612
|
</div>
|
|
4613
|
+
|
|
4614
|
+
<!-- Slide Content -->
|
|
4615
|
+
${contentHtml}
|
|
4299
4616
|
</div>
|
|
4300
4617
|
`;
|
|
4301
4618
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4302
4619
|
};
|
|
4303
4620
|
renderSlide(1);
|
|
4304
4621
|
const cleanup = () => {
|
|
4622
|
+
for (const u of createdBlobUrls) {
|
|
4623
|
+
URL.revokeObjectURL(u);
|
|
4624
|
+
}
|
|
4305
4625
|
container.remove();
|
|
4306
4626
|
ctx.container.innerHTML = "";
|
|
4307
4627
|
};
|
|
@@ -4341,13 +4661,48 @@ var PptPlugin = class {
|
|
|
4341
4661
|
if (!ctx2d) return;
|
|
4342
4662
|
canvas.width = 160;
|
|
4343
4663
|
canvas.height = 90;
|
|
4344
|
-
ctx2d.fillStyle = "#
|
|
4664
|
+
ctx2d.fillStyle = "#334155";
|
|
4345
4665
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4346
|
-
ctx2d.fillStyle = "#
|
|
4347
|
-
ctx2d.
|
|
4348
|
-
ctx2d.
|
|
4349
|
-
|
|
4350
|
-
ctx2d.
|
|
4666
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4667
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
4668
|
+
ctx2d.fillStyle = "#84cc16";
|
|
4669
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
4670
|
+
ctx2d.fillStyle = "#1e293b";
|
|
4671
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
4672
|
+
ctx2d.textAlign = "left";
|
|
4673
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
4674
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
4675
|
+
if (s.subtitle) {
|
|
4676
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
4677
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
4678
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4679
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
4680
|
+
ctx2d.textAlign = "center";
|
|
4681
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
4682
|
+
}
|
|
4683
|
+
if (s.pictureUrl) {
|
|
4684
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
4685
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
4686
|
+
ctx2d.fillStyle = "#ffffff";
|
|
4687
|
+
ctx2d.font = "8px sans-serif";
|
|
4688
|
+
ctx2d.textAlign = "center";
|
|
4689
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
4690
|
+
} else if (s.tableColumns.length > 0) {
|
|
4691
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
4692
|
+
ctx2d.lineWidth = 1;
|
|
4693
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
4694
|
+
for (let l = 1; l <= 3; l++) {
|
|
4695
|
+
ctx2d.beginPath();
|
|
4696
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
4697
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
4698
|
+
ctx2d.stroke();
|
|
4699
|
+
}
|
|
4700
|
+
} else {
|
|
4701
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
4702
|
+
for (let l = 0; l < 4; l++) {
|
|
4703
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4351
4706
|
}
|
|
4352
4707
|
}));
|
|
4353
4708
|
},
|
|
@@ -4365,66 +4720,165 @@ var PptPlugin = class {
|
|
|
4365
4720
|
}
|
|
4366
4721
|
};
|
|
4367
4722
|
}
|
|
4723
|
+
/**
|
|
4724
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
4725
|
+
*/
|
|
4726
|
+
extractPictures(cfbf, createdUrls) {
|
|
4727
|
+
const urls = [];
|
|
4728
|
+
try {
|
|
4729
|
+
const picStream = cfbf.readStream("Pictures");
|
|
4730
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
4731
|
+
const pBuf = new Uint8Array(picStream);
|
|
4732
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
4733
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
4734
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
4735
|
+
let match = true;
|
|
4736
|
+
for (let j = 0; j < 8; j++) {
|
|
4737
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
4738
|
+
match = false;
|
|
4739
|
+
break;
|
|
4740
|
+
}
|
|
4741
|
+
}
|
|
4742
|
+
if (match) {
|
|
4743
|
+
let endIdx = -1;
|
|
4744
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
4745
|
+
let endMatch = true;
|
|
4746
|
+
for (let j = 0; j < 8; j++) {
|
|
4747
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
4748
|
+
endMatch = false;
|
|
4749
|
+
break;
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
if (endMatch) {
|
|
4753
|
+
endIdx = k + 8;
|
|
4754
|
+
break;
|
|
4755
|
+
}
|
|
4756
|
+
}
|
|
4757
|
+
if (endIdx !== -1) {
|
|
4758
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
4759
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
4760
|
+
const url = URL.createObjectURL(blob);
|
|
4761
|
+
urls.push(url);
|
|
4762
|
+
createdUrls.push(url);
|
|
4763
|
+
i = endIdx;
|
|
4764
|
+
}
|
|
4765
|
+
}
|
|
4766
|
+
}
|
|
4767
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
4768
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
4769
|
+
let endIdx = -1;
|
|
4770
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
4771
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
4772
|
+
endIdx = k + 2;
|
|
4773
|
+
break;
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
if (endIdx !== -1) {
|
|
4777
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
4778
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
4779
|
+
const url = URL.createObjectURL(blob);
|
|
4780
|
+
urls.push(url);
|
|
4781
|
+
createdUrls.push(url);
|
|
4782
|
+
i = endIdx;
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
} catch (err) {
|
|
4787
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
4788
|
+
}
|
|
4789
|
+
return urls;
|
|
4790
|
+
}
|
|
4368
4791
|
/**
|
|
4369
4792
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4370
4793
|
*/
|
|
4371
|
-
extractSlides(stream) {
|
|
4794
|
+
extractSlides(stream, pictures) {
|
|
4372
4795
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4373
4796
|
const len = stream.length;
|
|
4374
4797
|
let offset = 0;
|
|
4375
4798
|
const slides = [];
|
|
4376
|
-
let
|
|
4799
|
+
let picIdx = 0;
|
|
4377
4800
|
while (offset + 8 <= len) {
|
|
4378
4801
|
const recVerInst = view.getUint16(offset, true);
|
|
4379
4802
|
const recType = view.getUint16(offset + 2, true);
|
|
4380
4803
|
const recLen = view.getUint32(offset + 4, true);
|
|
4804
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4381
4805
|
if (recType === 1006) {
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4806
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
4807
|
+
const rawTexts = [];
|
|
4808
|
+
let hasOle = false;
|
|
4809
|
+
let sOff = offset + 8;
|
|
4810
|
+
while (sOff + 8 <= slideEnd) {
|
|
4811
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
4812
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
4813
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
4814
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
4815
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4816
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4817
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
4818
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4819
|
+
rawTexts.push(txt);
|
|
4820
|
+
}
|
|
4821
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
4822
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
4823
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4824
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
4825
|
+
rawTexts.push(txt);
|
|
4826
|
+
}
|
|
4827
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
4828
|
+
hasOle = true;
|
|
4829
|
+
}
|
|
4830
|
+
if (cIsContainer) sOff += 8;
|
|
4831
|
+
else sOff += 8 + cLen;
|
|
4387
4832
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4833
|
+
let title = "";
|
|
4834
|
+
let subtitle = "";
|
|
4835
|
+
const paragraphs = [];
|
|
4836
|
+
const tableColumns = [];
|
|
4837
|
+
for (const t of rawTexts) {
|
|
4838
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
4839
|
+
title = t;
|
|
4840
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
4841
|
+
tableColumns.push(t);
|
|
4842
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
4843
|
+
subtitle = t;
|
|
4844
|
+
} else {
|
|
4845
|
+
paragraphs.push(t);
|
|
4846
|
+
}
|
|
4396
4847
|
}
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4401
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4402
|
-
currentSlideTexts.push(text);
|
|
4848
|
+
let pictureUrl = null;
|
|
4849
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
4850
|
+
pictureUrl = pictures[picIdx++];
|
|
4403
4851
|
}
|
|
4852
|
+
slides.push({
|
|
4853
|
+
slideIndex: slides.length + 1,
|
|
4854
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
4855
|
+
subtitle,
|
|
4856
|
+
paragraphs,
|
|
4857
|
+
tableColumns,
|
|
4858
|
+
pictureUrl,
|
|
4859
|
+
hasOle
|
|
4860
|
+
});
|
|
4404
4861
|
}
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
offset += 8;
|
|
4408
|
-
} else {
|
|
4409
|
-
offset += 8 + recLen;
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
if (currentSlideTexts.length > 0) {
|
|
4413
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4414
|
-
const texts = currentSlideTexts.slice(1);
|
|
4415
|
-
slides.push({ title, texts });
|
|
4862
|
+
if (isContainer) offset += 8;
|
|
4863
|
+
else offset += 8 + recLen;
|
|
4416
4864
|
}
|
|
4417
4865
|
if (slides.length === 0) {
|
|
4418
4866
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4419
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4420
|
-
const
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4867
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
4868
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
4869
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
4870
|
+
);
|
|
4871
|
+
if (clean.length > 0) {
|
|
4872
|
+
const chunkSize = 3;
|
|
4873
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
4874
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4425
4875
|
slides.push({
|
|
4876
|
+
slideIndex: slides.length + 1,
|
|
4426
4877
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4427
|
-
|
|
4878
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
4879
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
4880
|
+
tableColumns: [],
|
|
4881
|
+
pictureUrl: pictures[slides.length] || null
|
|
4428
4882
|
});
|
|
4429
4883
|
}
|
|
4430
4884
|
}
|