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