@files-preview-app/preview-file 1.2.3 → 1.2.5
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 +983 -245
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +980 -244
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +983 -245
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +980 -244
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +983 -245
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +980 -244
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +983 -245
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +980 -244
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import DOMPurify2 from 'dompurify';
|
|
2
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
2
3
|
import * as docx from 'docx-preview';
|
|
3
4
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
4
5
|
import * as XLSX from 'xlsx';
|
|
@@ -9,7 +10,7 @@ import * as THREE from 'three';
|
|
|
9
10
|
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
|
|
10
11
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
11
12
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
12
|
-
import
|
|
13
|
+
import * as RTFJS from 'rtf.js/dist/RTFJS.bundle.js';
|
|
13
14
|
|
|
14
15
|
// ../core/dist/index.js
|
|
15
16
|
var EventEmitter = class {
|
|
@@ -265,6 +266,39 @@ function detectOoxmlType(buffer) {
|
|
|
265
266
|
}
|
|
266
267
|
return "application/zip";
|
|
267
268
|
}
|
|
269
|
+
function detectCfbfType(buffer) {
|
|
270
|
+
const bytes = new Uint8Array(buffer);
|
|
271
|
+
const hasUtf16le = (str) => {
|
|
272
|
+
const target = new Uint8Array(str.length * 2);
|
|
273
|
+
for (let i = 0; i < str.length; i++) {
|
|
274
|
+
target[i * 2] = str.charCodeAt(i);
|
|
275
|
+
target[i * 2 + 1] = 0;
|
|
276
|
+
}
|
|
277
|
+
const targetLen = target.length;
|
|
278
|
+
const max = bytes.length - targetLen;
|
|
279
|
+
for (let i = 0; i <= max; i++) {
|
|
280
|
+
let match = true;
|
|
281
|
+
for (let j = 0; j < targetLen; j++) {
|
|
282
|
+
if (bytes[i + j] !== target[j]) {
|
|
283
|
+
match = false;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (match) return true;
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
290
|
+
};
|
|
291
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
292
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
293
|
+
}
|
|
294
|
+
if (hasUtf16le("WordDocument")) {
|
|
295
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
296
|
+
}
|
|
297
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
298
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
268
302
|
function extractExtension(nameOrUrl) {
|
|
269
303
|
try {
|
|
270
304
|
const url = new URL(nameOrUrl);
|
|
@@ -329,6 +363,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
329
363
|
} else {
|
|
330
364
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
331
365
|
}
|
|
366
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
367
|
+
if (metadata.extension) {
|
|
368
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
369
|
+
} else {
|
|
370
|
+
const cfbf = detectCfbfType(buffer);
|
|
371
|
+
if (cfbf) {
|
|
372
|
+
metadata.mimeType = cfbf.mime;
|
|
373
|
+
metadata.extension = cfbf.extension;
|
|
374
|
+
} else {
|
|
375
|
+
metadata.mimeType = magicMime;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
332
378
|
} else {
|
|
333
379
|
metadata.mimeType = magicMime;
|
|
334
380
|
}
|
|
@@ -477,11 +523,21 @@ var ToolbarController = class {
|
|
|
477
523
|
el;
|
|
478
524
|
toolbarEl;
|
|
479
525
|
actions = [];
|
|
526
|
+
pageInputEl = null;
|
|
527
|
+
pageLabelEl = null;
|
|
480
528
|
constructor(container) {
|
|
481
529
|
this.el = container;
|
|
482
530
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
483
531
|
this.el.appendChild(this.toolbarEl);
|
|
484
532
|
}
|
|
533
|
+
setPage(page, max) {
|
|
534
|
+
if (this.pageInputEl) {
|
|
535
|
+
this.pageInputEl.value = page.toString();
|
|
536
|
+
}
|
|
537
|
+
if (max !== void 0 && this.pageLabelEl) {
|
|
538
|
+
this.pageLabelEl.textContent = ` / ${max}`;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
485
541
|
update(actions) {
|
|
486
542
|
this.actions = actions;
|
|
487
543
|
this.render();
|
|
@@ -524,6 +580,7 @@ var ToolbarController = class {
|
|
|
524
580
|
if (action.type === "separator") {
|
|
525
581
|
groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
|
|
526
582
|
} else if (action.type === "page-nav") {
|
|
583
|
+
const max = action.max ?? 1;
|
|
527
584
|
const prevBtn = this.createButton(
|
|
528
585
|
"prev",
|
|
529
586
|
ICON_PAGE_PREV,
|
|
@@ -542,7 +599,6 @@ var ToolbarController = class {
|
|
|
542
599
|
"Next Page",
|
|
543
600
|
() => {
|
|
544
601
|
const cur = parseInt(input.value, 10) || 1;
|
|
545
|
-
const max = action.max ?? 1;
|
|
546
602
|
if (cur < max) {
|
|
547
603
|
input.value = (cur + 1).toString();
|
|
548
604
|
action.execute("next", cur + 1);
|
|
@@ -554,15 +610,18 @@ var ToolbarController = class {
|
|
|
554
610
|
type: "number",
|
|
555
611
|
value: (action.value ?? 1).toString(),
|
|
556
612
|
min: "1",
|
|
557
|
-
max:
|
|
613
|
+
max: max.toString()
|
|
558
614
|
});
|
|
559
615
|
input.addEventListener("change", () => {
|
|
560
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
563
|
-
|
|
616
|
+
let val = parseInt(input.value, 10);
|
|
617
|
+
if (isNaN(val)) val = 1;
|
|
618
|
+
val = Math.max(1, Math.min(max, val));
|
|
619
|
+
input.value = val.toString();
|
|
620
|
+
action.execute("go", val);
|
|
564
621
|
});
|
|
565
|
-
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${
|
|
622
|
+
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
623
|
+
this.pageInputEl = input;
|
|
624
|
+
this.pageLabelEl = label;
|
|
566
625
|
groupEl.appendChild(prevBtn);
|
|
567
626
|
groupEl.appendChild(input);
|
|
568
627
|
groupEl.appendChild(label);
|
|
@@ -740,7 +799,13 @@ var FilePreviewViewer = class {
|
|
|
740
799
|
buffer,
|
|
741
800
|
options,
|
|
742
801
|
signal,
|
|
743
|
-
emit: (event, payload) =>
|
|
802
|
+
emit: (event, payload) => {
|
|
803
|
+
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
804
|
+
const total = payload.total ?? payload.totalPages;
|
|
805
|
+
this.toolbar?.setPage(payload.page, total);
|
|
806
|
+
}
|
|
807
|
+
this.eventEmitter.emit(event, payload);
|
|
808
|
+
}
|
|
744
809
|
});
|
|
745
810
|
this.activeInstance = instance;
|
|
746
811
|
this.hideLoading();
|
|
@@ -774,6 +839,7 @@ var FilePreviewViewer = class {
|
|
|
774
839
|
if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
|
|
775
840
|
this.thumbnailPanel.update(thumbnails, (index) => {
|
|
776
841
|
instance.goToPage?.(index + 1);
|
|
842
|
+
this.toolbar?.setPage(index + 1);
|
|
777
843
|
});
|
|
778
844
|
if (options.showThumbnails) {
|
|
779
845
|
this.thumbnailPanel.show();
|
|
@@ -899,9 +965,13 @@ var FilePreviewViewer = class {
|
|
|
899
965
|
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
900
966
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
901
967
|
this.activeInstance.goToPage?.(cur + 1);
|
|
968
|
+
const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
|
|
969
|
+
this.toolbar?.setPage(nextCur);
|
|
902
970
|
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
903
971
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
904
972
|
this.activeInstance.goToPage?.(Math.max(1, cur - 1));
|
|
973
|
+
const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
|
|
974
|
+
this.toolbar?.setPage(prevCur);
|
|
905
975
|
} else if (e.key === "+" || e.key === "=") {
|
|
906
976
|
this.activeInstance.zoomIn?.();
|
|
907
977
|
} else if (e.key === "-" || e.key === "_") {
|
|
@@ -1167,30 +1237,62 @@ var CfbfReader = class {
|
|
|
1167
1237
|
return result.subarray(0, targetSize);
|
|
1168
1238
|
}
|
|
1169
1239
|
};
|
|
1170
|
-
|
|
1171
|
-
|
|
1240
|
+
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1241
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1242
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1172
1245
|
var PdfPlugin = class {
|
|
1173
1246
|
id = "pdf";
|
|
1174
|
-
name = "PDF Preview";
|
|
1247
|
+
name = "PDF Document Preview";
|
|
1175
1248
|
extensions = [".pdf"];
|
|
1176
1249
|
mimeTypes = ["application/pdf"];
|
|
1177
1250
|
weight = 100;
|
|
1178
1251
|
supports(file) {
|
|
1179
1252
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1180
1253
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1181
|
-
|
|
1254
|
+
if (ext) return this.extensions.includes(ext);
|
|
1255
|
+
return this.mimeTypes.includes(mime || "");
|
|
1182
1256
|
}
|
|
1183
1257
|
getToolbarActions(instance) {
|
|
1258
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1259
|
+
const curPage = instance.getCurrentPage?.() ?? 1;
|
|
1184
1260
|
return [
|
|
1261
|
+
{
|
|
1262
|
+
id: "thumbnails",
|
|
1263
|
+
icon: "thumbnails",
|
|
1264
|
+
label: "Page Thumbnails",
|
|
1265
|
+
type: "button",
|
|
1266
|
+
group: "navigation",
|
|
1267
|
+
execute: () => instance.toggleThumbnails?.()
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
id: "page-nav",
|
|
1271
|
+
icon: "",
|
|
1272
|
+
label: "Page Navigation",
|
|
1273
|
+
type: "page-nav",
|
|
1274
|
+
group: "navigation",
|
|
1275
|
+
value: curPage,
|
|
1276
|
+
max: totalPages,
|
|
1277
|
+
execute: (action, page) => {
|
|
1278
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1279
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1280
|
+
if (action === "prev") {
|
|
1281
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1282
|
+
} else if (action === "next") {
|
|
1283
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1284
|
+
} else if (typeof page === "number") {
|
|
1285
|
+
instance.goToPage?.(page);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1185
1289
|
{
|
|
1186
1290
|
id: "zoom-out",
|
|
1187
1291
|
icon: "zoom-out",
|
|
1188
1292
|
label: "Zoom Out",
|
|
1189
1293
|
type: "button",
|
|
1190
1294
|
group: "zoom",
|
|
1191
|
-
execute: () =>
|
|
1192
|
-
instance.zoomOut?.();
|
|
1193
|
-
}
|
|
1295
|
+
execute: () => instance.zoomOut?.()
|
|
1194
1296
|
},
|
|
1195
1297
|
{
|
|
1196
1298
|
id: "zoom-in",
|
|
@@ -1198,9 +1300,7 @@ var PdfPlugin = class {
|
|
|
1198
1300
|
label: "Zoom In",
|
|
1199
1301
|
type: "button",
|
|
1200
1302
|
group: "zoom",
|
|
1201
|
-
execute: () =>
|
|
1202
|
-
instance.zoomIn?.();
|
|
1203
|
-
}
|
|
1303
|
+
execute: () => instance.zoomIn?.()
|
|
1204
1304
|
},
|
|
1205
1305
|
{
|
|
1206
1306
|
id: "fit-page",
|
|
@@ -1208,39 +1308,23 @@ var PdfPlugin = class {
|
|
|
1208
1308
|
label: "Fit to Page",
|
|
1209
1309
|
type: "button",
|
|
1210
1310
|
group: "zoom",
|
|
1211
|
-
execute: () =>
|
|
1212
|
-
instance.fitToPage?.();
|
|
1213
|
-
}
|
|
1311
|
+
execute: () => instance.fitToPage?.()
|
|
1214
1312
|
},
|
|
1215
1313
|
{
|
|
1216
1314
|
id: "rotate-cw",
|
|
1217
1315
|
icon: "rotate-cw",
|
|
1218
|
-
label: "Rotate",
|
|
1316
|
+
label: "Rotate Clockwise",
|
|
1219
1317
|
type: "button",
|
|
1220
1318
|
group: "view",
|
|
1221
|
-
execute: () =>
|
|
1222
|
-
instance.rotateCW?.();
|
|
1223
|
-
}
|
|
1224
|
-
},
|
|
1225
|
-
{
|
|
1226
|
-
id: "page-nav",
|
|
1227
|
-
icon: "page-nav",
|
|
1228
|
-
label: "Page Navigation",
|
|
1229
|
-
type: "page-nav",
|
|
1230
|
-
group: "navigation",
|
|
1231
|
-
execute: (page) => {
|
|
1232
|
-
if (typeof page === "number") instance.goToPage?.(page);
|
|
1233
|
-
}
|
|
1319
|
+
execute: () => instance.rotateCW?.()
|
|
1234
1320
|
},
|
|
1235
1321
|
{
|
|
1236
1322
|
id: "download",
|
|
1237
1323
|
icon: "download",
|
|
1238
|
-
label: "Download",
|
|
1324
|
+
label: "Download PDF",
|
|
1239
1325
|
type: "button",
|
|
1240
1326
|
group: "actions",
|
|
1241
|
-
execute: () =>
|
|
1242
|
-
instance.download?.();
|
|
1243
|
-
}
|
|
1327
|
+
execute: () => instance.download?.()
|
|
1244
1328
|
},
|
|
1245
1329
|
{
|
|
1246
1330
|
id: "print",
|
|
@@ -1248,99 +1332,213 @@ var PdfPlugin = class {
|
|
|
1248
1332
|
label: "Print",
|
|
1249
1333
|
type: "button",
|
|
1250
1334
|
group: "actions",
|
|
1251
|
-
execute: () =>
|
|
1252
|
-
instance.print?.();
|
|
1253
|
-
}
|
|
1335
|
+
execute: () => instance.print?.()
|
|
1254
1336
|
}
|
|
1255
1337
|
];
|
|
1256
1338
|
}
|
|
1257
1339
|
async render(ctx) {
|
|
1258
|
-
const
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1340
|
+
const container = document.createElement("div");
|
|
1341
|
+
container.className = "fp-pdf-container";
|
|
1342
|
+
container.style.width = "100%";
|
|
1343
|
+
container.style.height = "100%";
|
|
1344
|
+
container.style.overflow = "auto";
|
|
1345
|
+
container.style.display = "flex";
|
|
1346
|
+
container.style.flexDirection = "column";
|
|
1347
|
+
container.style.alignItems = "center";
|
|
1348
|
+
container.style.padding = "24px 16px";
|
|
1349
|
+
container.style.backgroundColor = "#0f172a";
|
|
1350
|
+
container.style.boxSizing = "border-box";
|
|
1351
|
+
container.style.position = "relative";
|
|
1352
|
+
const pageCard = document.createElement("div");
|
|
1353
|
+
pageCard.className = "fp-pdf-page-card";
|
|
1354
|
+
pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
|
|
1355
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
1356
|
+
pageCard.style.borderRadius = "4px";
|
|
1357
|
+
pageCard.style.overflow = "hidden";
|
|
1358
|
+
pageCard.style.lineHeight = "0";
|
|
1359
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
1360
|
+
pageCard.style.position = "relative";
|
|
1361
|
+
const canvas = document.createElement("canvas");
|
|
1362
|
+
pageCard.appendChild(canvas);
|
|
1363
|
+
container.appendChild(pageCard);
|
|
1364
|
+
const indicator = document.createElement("div");
|
|
1365
|
+
indicator.className = "fp-pdf-page-indicator";
|
|
1366
|
+
indicator.style.position = "sticky";
|
|
1367
|
+
indicator.style.bottom = "16px";
|
|
1368
|
+
indicator.style.marginTop = "16px";
|
|
1369
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1370
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1371
|
+
indicator.style.color = "#f8fafc";
|
|
1372
|
+
indicator.style.fontSize = "12px";
|
|
1373
|
+
indicator.style.fontWeight = "600";
|
|
1374
|
+
indicator.style.padding = "5px 14px";
|
|
1375
|
+
indicator.style.borderRadius = "20px";
|
|
1376
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1377
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1378
|
+
indicator.style.zIndex = "10";
|
|
1379
|
+
indicator.style.userSelect = "none";
|
|
1380
|
+
indicator.style.pointerEvents = "none";
|
|
1381
|
+
container.appendChild(indicator);
|
|
1382
|
+
ctx.container.appendChild(container);
|
|
1383
|
+
const loadingTask = pdfjsLib.getDocument({
|
|
1384
|
+
data: new Uint8Array(ctx.buffer),
|
|
1385
|
+
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
|
|
1386
|
+
cMapPacked: true,
|
|
1387
|
+
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
|
|
1388
|
+
});
|
|
1389
|
+
const pdfDoc = await loadingTask.promise;
|
|
1390
|
+
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1274
1391
|
let currentPage = 1;
|
|
1275
|
-
let
|
|
1392
|
+
let zoomScale = 1;
|
|
1276
1393
|
let rotation = 0;
|
|
1394
|
+
let currentRenderTask = null;
|
|
1395
|
+
const renderPage = async (pageNum) => {
|
|
1396
|
+
if (currentRenderTask) {
|
|
1397
|
+
try {
|
|
1398
|
+
currentRenderTask.cancel();
|
|
1399
|
+
} catch {
|
|
1400
|
+
}
|
|
1401
|
+
currentRenderTask = null;
|
|
1402
|
+
}
|
|
1403
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1404
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1405
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1406
|
+
const page = await pdfDoc.getPage(currentPage);
|
|
1407
|
+
const containerWidth = container.clientWidth || 900;
|
|
1408
|
+
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1409
|
+
const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
|
|
1410
|
+
const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
|
|
1411
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
1412
|
+
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
1413
|
+
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1414
|
+
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1415
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
1416
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
1417
|
+
const canvasCtx = canvas.getContext("2d");
|
|
1418
|
+
if (!canvasCtx) return;
|
|
1419
|
+
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
1420
|
+
currentRenderTask = page.render({
|
|
1421
|
+
canvasContext: canvasCtx,
|
|
1422
|
+
viewport
|
|
1423
|
+
});
|
|
1424
|
+
try {
|
|
1425
|
+
await currentRenderTask.promise;
|
|
1426
|
+
} catch (err) {
|
|
1427
|
+
if (err?.name !== "RenderingCancelledException") {
|
|
1428
|
+
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1429
|
+
}
|
|
1430
|
+
} finally {
|
|
1431
|
+
currentRenderTask = null;
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
await renderPage(1);
|
|
1277
1435
|
const cleanup = () => {
|
|
1278
|
-
|
|
1279
|
-
|
|
1436
|
+
if (currentRenderTask) {
|
|
1437
|
+
try {
|
|
1438
|
+
currentRenderTask.cancel();
|
|
1439
|
+
} catch {
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
try {
|
|
1443
|
+
pdfDoc.destroy();
|
|
1444
|
+
} catch {
|
|
1445
|
+
}
|
|
1446
|
+
container.remove();
|
|
1280
1447
|
ctx.container.innerHTML = "";
|
|
1281
1448
|
};
|
|
1282
1449
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1283
|
-
|
|
1450
|
+
const instance = {
|
|
1284
1451
|
destroy: cleanup,
|
|
1285
1452
|
zoomIn: () => {
|
|
1286
|
-
|
|
1287
|
-
|
|
1453
|
+
zoomScale = Math.min(3.5, zoomScale + 0.2);
|
|
1454
|
+
renderPage(currentPage);
|
|
1288
1455
|
},
|
|
1289
1456
|
zoomOut: () => {
|
|
1290
|
-
|
|
1291
|
-
|
|
1457
|
+
zoomScale = Math.max(0.3, zoomScale - 0.2);
|
|
1458
|
+
renderPage(currentPage);
|
|
1292
1459
|
},
|
|
1293
|
-
getZoom: () =>
|
|
1460
|
+
getZoom: () => zoomScale,
|
|
1294
1461
|
setZoom: (level) => {
|
|
1295
|
-
|
|
1296
|
-
|
|
1462
|
+
zoomScale = Math.max(0.3, Math.min(3.5, level));
|
|
1463
|
+
renderPage(currentPage);
|
|
1297
1464
|
},
|
|
1298
1465
|
fitToPage: () => {
|
|
1299
|
-
|
|
1300
|
-
|
|
1466
|
+
zoomScale = 1;
|
|
1467
|
+
renderPage(currentPage);
|
|
1301
1468
|
},
|
|
1302
1469
|
rotateCW: () => {
|
|
1303
1470
|
rotation = (rotation + 90) % 360;
|
|
1304
|
-
|
|
1471
|
+
renderPage(currentPage);
|
|
1305
1472
|
},
|
|
1306
1473
|
rotateCCW: () => {
|
|
1307
1474
|
rotation = (rotation - 90 + 360) % 360;
|
|
1308
|
-
|
|
1475
|
+
renderPage(currentPage);
|
|
1309
1476
|
},
|
|
1310
1477
|
getRotation: () => rotation,
|
|
1478
|
+
getPageCount: () => totalPages,
|
|
1479
|
+
getCurrentPage: () => currentPage,
|
|
1311
1480
|
goToPage: (page) => {
|
|
1312
|
-
|
|
1313
|
-
iframe.src = `${url}#page=${page}`;
|
|
1481
|
+
renderPage(page);
|
|
1314
1482
|
},
|
|
1315
|
-
getCurrentPage: () => currentPage,
|
|
1316
1483
|
download: () => {
|
|
1484
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1485
|
+
const url = URL.createObjectURL(blob);
|
|
1317
1486
|
const a = document.createElement("a");
|
|
1318
1487
|
a.href = url;
|
|
1319
1488
|
a.download = ctx.metadata.name || "document.pdf";
|
|
1320
1489
|
a.click();
|
|
1490
|
+
URL.revokeObjectURL(url);
|
|
1321
1491
|
},
|
|
1322
1492
|
print: () => {
|
|
1323
|
-
|
|
1493
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1494
|
+
const url = URL.createObjectURL(blob);
|
|
1495
|
+
const hiddenIframe = document.createElement("iframe");
|
|
1496
|
+
hiddenIframe.style.position = "fixed";
|
|
1497
|
+
hiddenIframe.style.right = "0";
|
|
1498
|
+
hiddenIframe.style.bottom = "0";
|
|
1499
|
+
hiddenIframe.style.width = "0";
|
|
1500
|
+
hiddenIframe.style.height = "0";
|
|
1501
|
+
hiddenIframe.style.border = "0";
|
|
1502
|
+
document.body.appendChild(hiddenIframe);
|
|
1503
|
+
hiddenIframe.src = url;
|
|
1504
|
+
hiddenIframe.onload = () => {
|
|
1505
|
+
setTimeout(() => {
|
|
1506
|
+
hiddenIframe.contentWindow?.print();
|
|
1507
|
+
setTimeout(() => {
|
|
1508
|
+
hiddenIframe.remove();
|
|
1509
|
+
URL.revokeObjectURL(url);
|
|
1510
|
+
}, 1e3);
|
|
1511
|
+
}, 300);
|
|
1512
|
+
};
|
|
1324
1513
|
},
|
|
1325
1514
|
getThumbnails: async () => {
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1515
|
+
const thumbnails = [];
|
|
1516
|
+
const count = Math.min(totalPages, 50);
|
|
1517
|
+
for (let i = 1; i <= count; i++) {
|
|
1518
|
+
thumbnails.push({
|
|
1519
|
+
index: i,
|
|
1520
|
+
label: `Page ${i}`,
|
|
1521
|
+
render: async (thumbCanvas) => {
|
|
1522
|
+
try {
|
|
1523
|
+
const p = await pdfDoc.getPage(i);
|
|
1524
|
+
const baseVp = p.getViewport({ scale: 1 });
|
|
1525
|
+
const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
|
|
1526
|
+
const thumbVp = p.getViewport({ scale: thumbScale });
|
|
1527
|
+
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1528
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
1529
|
+
if (tCtx) {
|
|
1530
|
+
await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
|
|
1531
|
+
}
|
|
1532
|
+
} catch (e) {
|
|
1533
|
+
console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
|
|
1338
1534
|
}
|
|
1339
1535
|
}
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return thumbnails;
|
|
1342
1539
|
}
|
|
1343
1540
|
};
|
|
1541
|
+
return instance;
|
|
1344
1542
|
}
|
|
1345
1543
|
};
|
|
1346
1544
|
function pdfPlugin() {
|
|
@@ -1661,10 +1859,37 @@ var DocxPlugin = class {
|
|
|
1661
1859
|
supports(file) {
|
|
1662
1860
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1663
1861
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1664
|
-
|
|
1862
|
+
if (ext) {
|
|
1863
|
+
return this.extensions.includes(ext);
|
|
1864
|
+
}
|
|
1865
|
+
return this.mimeTypes.includes(mime || "");
|
|
1665
1866
|
}
|
|
1666
1867
|
getToolbarActions(instance) {
|
|
1667
|
-
|
|
1868
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1869
|
+
const actions = [];
|
|
1870
|
+
if (totalPages > 1) {
|
|
1871
|
+
actions.push({
|
|
1872
|
+
id: "page-nav",
|
|
1873
|
+
icon: "",
|
|
1874
|
+
label: "Page Navigation",
|
|
1875
|
+
type: "page-nav",
|
|
1876
|
+
group: "navigation",
|
|
1877
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
1878
|
+
max: totalPages,
|
|
1879
|
+
execute: (action, page) => {
|
|
1880
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1881
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1882
|
+
if (action === "prev") {
|
|
1883
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1884
|
+
} else if (action === "next") {
|
|
1885
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1886
|
+
} else if (typeof page === "number") {
|
|
1887
|
+
instance.goToPage?.(page);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
actions.push(
|
|
1668
1893
|
{
|
|
1669
1894
|
id: "zoom-out",
|
|
1670
1895
|
icon: "zoom-out",
|
|
@@ -1705,7 +1930,8 @@ var DocxPlugin = class {
|
|
|
1705
1930
|
group: "actions",
|
|
1706
1931
|
execute: () => instance.print?.()
|
|
1707
1932
|
}
|
|
1708
|
-
|
|
1933
|
+
);
|
|
1934
|
+
return actions;
|
|
1709
1935
|
}
|
|
1710
1936
|
async render(ctx) {
|
|
1711
1937
|
const wrapper = document.createElement("div");
|
|
@@ -1781,7 +2007,51 @@ var DocxPlugin = class {
|
|
|
1781
2007
|
}
|
|
1782
2008
|
}
|
|
1783
2009
|
}
|
|
2010
|
+
const sections = wrapper.querySelectorAll("section.docx");
|
|
2011
|
+
const cards = wrapper.querySelectorAll(".fp-docx-page-card");
|
|
2012
|
+
const pageElements = sections.length > 0 ? sections : cards;
|
|
2013
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
2014
|
+
let currentPage = 1;
|
|
2015
|
+
let indicator = null;
|
|
2016
|
+
if (totalPages > 1) {
|
|
2017
|
+
indicator = document.createElement("div");
|
|
2018
|
+
indicator.className = "fp-docx-page-indicator";
|
|
2019
|
+
indicator.style.position = "sticky";
|
|
2020
|
+
indicator.style.bottom = "16px";
|
|
2021
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2022
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2023
|
+
indicator.style.color = "#f8fafc";
|
|
2024
|
+
indicator.style.fontSize = "12px";
|
|
2025
|
+
indicator.style.fontWeight = "600";
|
|
2026
|
+
indicator.style.padding = "5px 14px";
|
|
2027
|
+
indicator.style.borderRadius = "20px";
|
|
2028
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2029
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2030
|
+
indicator.style.zIndex = "10";
|
|
2031
|
+
indicator.style.userSelect = "none";
|
|
2032
|
+
indicator.style.pointerEvents = "none";
|
|
2033
|
+
indicator.style.textAlign = "center";
|
|
2034
|
+
indicator.style.width = "fit-content";
|
|
2035
|
+
indicator.style.margin = "16px auto 0";
|
|
2036
|
+
ctx.container.appendChild(indicator);
|
|
2037
|
+
}
|
|
2038
|
+
const showPage = (pageNum) => {
|
|
2039
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2040
|
+
if (pageElements.length > 1) {
|
|
2041
|
+
pageElements.forEach((sec, idx) => {
|
|
2042
|
+
sec.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
if (indicator) {
|
|
2046
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2047
|
+
}
|
|
2048
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2049
|
+
};
|
|
2050
|
+
if (totalPages > 1) {
|
|
2051
|
+
showPage(1);
|
|
2052
|
+
}
|
|
1784
2053
|
const cleanup = () => {
|
|
2054
|
+
indicator?.remove();
|
|
1785
2055
|
for (const url of createdBlobUrls) {
|
|
1786
2056
|
URL.revokeObjectURL(url);
|
|
1787
2057
|
}
|
|
@@ -1792,6 +2062,11 @@ var DocxPlugin = class {
|
|
|
1792
2062
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1793
2063
|
return {
|
|
1794
2064
|
destroy: cleanup,
|
|
2065
|
+
getPageCount: () => totalPages,
|
|
2066
|
+
getCurrentPage: () => currentPage,
|
|
2067
|
+
goToPage: (page) => {
|
|
2068
|
+
showPage(page);
|
|
2069
|
+
},
|
|
1795
2070
|
zoomIn: () => {
|
|
1796
2071
|
scale += 0.1;
|
|
1797
2072
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -2050,10 +2325,37 @@ var ExcelPlugin = class {
|
|
|
2050
2325
|
supports(file) {
|
|
2051
2326
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2052
2327
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2053
|
-
|
|
2328
|
+
if (ext) {
|
|
2329
|
+
return this.extensions.includes(ext);
|
|
2330
|
+
}
|
|
2331
|
+
return this.mimeTypes.includes(mime || "");
|
|
2054
2332
|
}
|
|
2055
2333
|
getToolbarActions(instance) {
|
|
2056
|
-
|
|
2334
|
+
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2335
|
+
const actions = [];
|
|
2336
|
+
if (totalSheets > 1) {
|
|
2337
|
+
actions.push({
|
|
2338
|
+
id: "page-nav",
|
|
2339
|
+
icon: "",
|
|
2340
|
+
label: "Sheet Navigation",
|
|
2341
|
+
type: "page-nav",
|
|
2342
|
+
group: "navigation",
|
|
2343
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2344
|
+
max: totalSheets,
|
|
2345
|
+
execute: (action, sheet) => {
|
|
2346
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2347
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2348
|
+
if (action === "prev") {
|
|
2349
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2350
|
+
} else if (action === "next") {
|
|
2351
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2352
|
+
} else if (typeof sheet === "number") {
|
|
2353
|
+
instance.goToPage?.(sheet);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
actions.push(
|
|
2057
2359
|
{
|
|
2058
2360
|
id: "zoom-out",
|
|
2059
2361
|
icon: "zoom-out",
|
|
@@ -2074,16 +2376,6 @@ var ExcelPlugin = class {
|
|
|
2074
2376
|
instance.zoomIn?.();
|
|
2075
2377
|
}
|
|
2076
2378
|
},
|
|
2077
|
-
{
|
|
2078
|
-
id: "page-nav",
|
|
2079
|
-
icon: "page-nav",
|
|
2080
|
-
label: "Sheet Navigation",
|
|
2081
|
-
type: "page-nav",
|
|
2082
|
-
group: "navigation",
|
|
2083
|
-
execute: (sheet) => {
|
|
2084
|
-
if (typeof sheet === "number") instance.goToPage?.(sheet);
|
|
2085
|
-
}
|
|
2086
|
-
},
|
|
2087
2379
|
{
|
|
2088
2380
|
id: "download",
|
|
2089
2381
|
icon: "download",
|
|
@@ -2104,7 +2396,8 @@ var ExcelPlugin = class {
|
|
|
2104
2396
|
instance.print?.();
|
|
2105
2397
|
}
|
|
2106
2398
|
}
|
|
2107
|
-
|
|
2399
|
+
);
|
|
2400
|
+
return actions;
|
|
2108
2401
|
}
|
|
2109
2402
|
async render(ctx) {
|
|
2110
2403
|
const container = document.createElement("div");
|
|
@@ -2188,6 +2481,7 @@ var ExcelPlugin = class {
|
|
|
2188
2481
|
b.style.fontWeight = "normal";
|
|
2189
2482
|
}
|
|
2190
2483
|
});
|
|
2484
|
+
ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
|
|
2191
2485
|
};
|
|
2192
2486
|
if (sheetNames.length > 0) {
|
|
2193
2487
|
sheetNames.forEach((name, idx) => {
|
|
@@ -2437,7 +2731,31 @@ var CodePlugin = class {
|
|
|
2437
2731
|
return false;
|
|
2438
2732
|
}
|
|
2439
2733
|
getToolbarActions(instance) {
|
|
2440
|
-
|
|
2734
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2735
|
+
const actions = [];
|
|
2736
|
+
if (totalPages > 1) {
|
|
2737
|
+
actions.push({
|
|
2738
|
+
id: "page-nav",
|
|
2739
|
+
icon: "",
|
|
2740
|
+
label: "Page Navigation",
|
|
2741
|
+
type: "page-nav",
|
|
2742
|
+
group: "navigation",
|
|
2743
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2744
|
+
max: totalPages,
|
|
2745
|
+
execute: (action, page) => {
|
|
2746
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2747
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2748
|
+
if (action === "prev") {
|
|
2749
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2750
|
+
} else if (action === "next") {
|
|
2751
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2752
|
+
} else if (typeof page === "number") {
|
|
2753
|
+
instance.goToPage?.(page);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
actions.push(
|
|
2441
2759
|
{
|
|
2442
2760
|
id: "zoom-out",
|
|
2443
2761
|
icon: "zoom-out",
|
|
@@ -2488,11 +2806,16 @@ var CodePlugin = class {
|
|
|
2488
2806
|
instance.print?.();
|
|
2489
2807
|
}
|
|
2490
2808
|
}
|
|
2491
|
-
|
|
2809
|
+
);
|
|
2810
|
+
return actions;
|
|
2492
2811
|
}
|
|
2493
2812
|
async render(ctx) {
|
|
2494
2813
|
const decoder = new TextDecoder("utf-8");
|
|
2495
|
-
const
|
|
2814
|
+
const fullText = decoder.decode(ctx.buffer);
|
|
2815
|
+
const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
|
|
2816
|
+
const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
2817
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
2818
|
+
let currentPage = 1;
|
|
2496
2819
|
const container = document.createElement("div");
|
|
2497
2820
|
container.style.width = "100%";
|
|
2498
2821
|
container.style.height = "100%";
|
|
@@ -2511,25 +2834,66 @@ var CodePlugin = class {
|
|
|
2511
2834
|
pre.style.wordBreak = "break-all";
|
|
2512
2835
|
const code = document.createElement("code");
|
|
2513
2836
|
const ext = (ctx.metadata.extension || "").replace(".", "");
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2837
|
+
const renderCodePage = (text) => {
|
|
2838
|
+
try {
|
|
2839
|
+
if (ext && hljs.getLanguage(ext)) {
|
|
2840
|
+
code.innerHTML = hljs.highlight(text, { language: ext }).value;
|
|
2841
|
+
} else {
|
|
2842
|
+
code.innerHTML = hljs.highlightAuto(text).value;
|
|
2843
|
+
}
|
|
2844
|
+
} catch {
|
|
2845
|
+
code.textContent = text;
|
|
2519
2846
|
}
|
|
2520
|
-
}
|
|
2521
|
-
|
|
2522
|
-
}
|
|
2847
|
+
};
|
|
2848
|
+
renderCodePage(rawPages[0] || fullText);
|
|
2523
2849
|
pre.appendChild(code);
|
|
2524
2850
|
container.appendChild(pre);
|
|
2851
|
+
let indicator = null;
|
|
2852
|
+
if (totalPages > 1) {
|
|
2853
|
+
indicator = document.createElement("div");
|
|
2854
|
+
indicator.className = "fp-code-page-indicator";
|
|
2855
|
+
indicator.style.position = "sticky";
|
|
2856
|
+
indicator.style.bottom = "16px";
|
|
2857
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2858
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2859
|
+
indicator.style.color = "#f8fafc";
|
|
2860
|
+
indicator.style.fontSize = "12px";
|
|
2861
|
+
indicator.style.fontWeight = "600";
|
|
2862
|
+
indicator.style.padding = "5px 14px";
|
|
2863
|
+
indicator.style.borderRadius = "20px";
|
|
2864
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2865
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2866
|
+
indicator.style.zIndex = "10";
|
|
2867
|
+
indicator.style.userSelect = "none";
|
|
2868
|
+
indicator.style.pointerEvents = "none";
|
|
2869
|
+
indicator.style.textAlign = "center";
|
|
2870
|
+
indicator.style.width = "fit-content";
|
|
2871
|
+
indicator.style.margin = "16px auto 0";
|
|
2872
|
+
container.appendChild(indicator);
|
|
2873
|
+
}
|
|
2874
|
+
const showPage = (pageNum) => {
|
|
2875
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2876
|
+
renderCodePage(rawPages[currentPage - 1] || fullText);
|
|
2877
|
+
if (indicator) {
|
|
2878
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2879
|
+
}
|
|
2880
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2881
|
+
};
|
|
2882
|
+
if (totalPages > 1) {
|
|
2883
|
+
showPage(1);
|
|
2884
|
+
}
|
|
2525
2885
|
ctx.container.appendChild(container);
|
|
2526
2886
|
const cleanup = () => {
|
|
2887
|
+
indicator?.remove();
|
|
2527
2888
|
container.remove();
|
|
2528
2889
|
ctx.container.innerHTML = "";
|
|
2529
2890
|
};
|
|
2530
2891
|
ctx.signal.addEventListener("abort", cleanup);
|
|
2531
2892
|
return {
|
|
2532
2893
|
destroy: cleanup,
|
|
2894
|
+
getPageCount: () => totalPages,
|
|
2895
|
+
getCurrentPage: () => currentPage,
|
|
2896
|
+
goToPage: (page) => showPage(page),
|
|
2533
2897
|
zoomIn: () => {
|
|
2534
2898
|
fontSize = Math.min(32, fontSize + 2);
|
|
2535
2899
|
pre.style.fontSize = `${fontSize}px`;
|
|
@@ -2557,7 +2921,7 @@ var CodePlugin = class {
|
|
|
2557
2921
|
window.print();
|
|
2558
2922
|
},
|
|
2559
2923
|
copy: () => {
|
|
2560
|
-
navigator.clipboard?.writeText(
|
|
2924
|
+
navigator.clipboard?.writeText(fullText);
|
|
2561
2925
|
}
|
|
2562
2926
|
};
|
|
2563
2927
|
}
|
|
@@ -2992,7 +3356,10 @@ var PptxPlugin = class {
|
|
|
2992
3356
|
supports(file) {
|
|
2993
3357
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2994
3358
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2995
|
-
|
|
3359
|
+
if (ext) {
|
|
3360
|
+
return this.extensions.includes(ext);
|
|
3361
|
+
}
|
|
3362
|
+
return this.mimeTypes.includes(mime || "");
|
|
2996
3363
|
}
|
|
2997
3364
|
getToolbarActions(instance) {
|
|
2998
3365
|
return [
|
|
@@ -3376,7 +3743,31 @@ var RtfPlugin = class {
|
|
|
3376
3743
|
return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
|
|
3377
3744
|
}
|
|
3378
3745
|
getToolbarActions(instance) {
|
|
3379
|
-
|
|
3746
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3747
|
+
const actions = [];
|
|
3748
|
+
if (totalPages > 1) {
|
|
3749
|
+
actions.push({
|
|
3750
|
+
id: "page-nav",
|
|
3751
|
+
icon: "",
|
|
3752
|
+
label: "Page Navigation",
|
|
3753
|
+
type: "page-nav",
|
|
3754
|
+
group: "navigation",
|
|
3755
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3756
|
+
max: totalPages,
|
|
3757
|
+
execute: (action, page) => {
|
|
3758
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3759
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3760
|
+
if (action === "prev") {
|
|
3761
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3762
|
+
} else if (action === "next") {
|
|
3763
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3764
|
+
} else if (typeof page === "number") {
|
|
3765
|
+
instance.goToPage?.(page);
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3770
|
+
actions.push(
|
|
3380
3771
|
{
|
|
3381
3772
|
id: "zoom-out",
|
|
3382
3773
|
icon: "zoom-out",
|
|
@@ -3417,7 +3808,8 @@ var RtfPlugin = class {
|
|
|
3417
3808
|
group: "actions",
|
|
3418
3809
|
execute: () => instance.print?.()
|
|
3419
3810
|
}
|
|
3420
|
-
|
|
3811
|
+
);
|
|
3812
|
+
return actions;
|
|
3421
3813
|
}
|
|
3422
3814
|
async render(ctx) {
|
|
3423
3815
|
const wrapper = document.createElement("div");
|
|
@@ -3436,25 +3828,82 @@ var RtfPlugin = class {
|
|
|
3436
3828
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
3437
3829
|
ctx.container.appendChild(wrapper);
|
|
3438
3830
|
let scale = 1;
|
|
3831
|
+
let pageElements = [];
|
|
3439
3832
|
try {
|
|
3833
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3834
|
+
RTFJS.loggingEnabled(false);
|
|
3835
|
+
}
|
|
3440
3836
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3441
3837
|
const htmlElements = await doc.render();
|
|
3442
|
-
|
|
3838
|
+
pageElements = htmlElements;
|
|
3839
|
+
for (let i = 0; i < htmlElements.length; i++) {
|
|
3840
|
+
const el = htmlElements[i];
|
|
3841
|
+
el.style.display = i === 0 ? "block" : "none";
|
|
3443
3842
|
wrapper.appendChild(el);
|
|
3444
3843
|
}
|
|
3445
3844
|
} catch (err) {
|
|
3446
3845
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
3447
3846
|
const text = new TextDecoder("latin1").decode(ctx.buffer);
|
|
3448
3847
|
const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
|
|
3449
|
-
|
|
3848
|
+
const pre = document.createElement("pre");
|
|
3849
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
3850
|
+
pre.style.fontFamily = "serif";
|
|
3851
|
+
pre.style.color = "#333";
|
|
3852
|
+
pre.textContent = clean;
|
|
3853
|
+
wrapper.appendChild(pre);
|
|
3854
|
+
pageElements = [pre];
|
|
3855
|
+
}
|
|
3856
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
3857
|
+
let currentPage = 1;
|
|
3858
|
+
let indicator = null;
|
|
3859
|
+
if (totalPages > 1) {
|
|
3860
|
+
indicator = document.createElement("div");
|
|
3861
|
+
indicator.className = "fp-rtf-page-indicator";
|
|
3862
|
+
indicator.style.position = "sticky";
|
|
3863
|
+
indicator.style.bottom = "16px";
|
|
3864
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3865
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
3866
|
+
indicator.style.color = "#f8fafc";
|
|
3867
|
+
indicator.style.fontSize = "12px";
|
|
3868
|
+
indicator.style.fontWeight = "600";
|
|
3869
|
+
indicator.style.padding = "5px 14px";
|
|
3870
|
+
indicator.style.borderRadius = "20px";
|
|
3871
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3872
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3873
|
+
indicator.style.zIndex = "10";
|
|
3874
|
+
indicator.style.userSelect = "none";
|
|
3875
|
+
indicator.style.pointerEvents = "none";
|
|
3876
|
+
indicator.style.textAlign = "center";
|
|
3877
|
+
indicator.style.width = "fit-content";
|
|
3878
|
+
indicator.style.margin = "16px auto 0";
|
|
3879
|
+
ctx.container.appendChild(indicator);
|
|
3880
|
+
}
|
|
3881
|
+
const showPage = (pageNum) => {
|
|
3882
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3883
|
+
if (totalPages > 1) {
|
|
3884
|
+
pageElements.forEach((el, idx) => {
|
|
3885
|
+
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
if (indicator) {
|
|
3889
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3890
|
+
}
|
|
3891
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3892
|
+
};
|
|
3893
|
+
if (totalPages > 1) {
|
|
3894
|
+
showPage(1);
|
|
3450
3895
|
}
|
|
3451
3896
|
const cleanup = () => {
|
|
3897
|
+
indicator?.remove();
|
|
3452
3898
|
wrapper.remove();
|
|
3453
3899
|
ctx.container.innerHTML = "";
|
|
3454
3900
|
};
|
|
3455
3901
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3456
3902
|
return {
|
|
3457
3903
|
destroy: cleanup,
|
|
3904
|
+
getPageCount: () => totalPages,
|
|
3905
|
+
getCurrentPage: () => currentPage,
|
|
3906
|
+
goToPage: (page) => showPage(page),
|
|
3458
3907
|
zoomIn: () => {
|
|
3459
3908
|
scale += 0.1;
|
|
3460
3909
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -3635,45 +4084,48 @@ var OpenDocumentPlugin = class {
|
|
|
3635
4084
|
supports(file) {
|
|
3636
4085
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3637
4086
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3638
|
-
|
|
4087
|
+
if (ext) {
|
|
4088
|
+
return this.extensions.includes(ext);
|
|
4089
|
+
}
|
|
4090
|
+
return this.mimeTypes.includes(mime || "");
|
|
3639
4091
|
}
|
|
3640
4092
|
getToolbarActions(instance) {
|
|
3641
4093
|
const isPresentation = instance.isPresentation;
|
|
4094
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3642
4095
|
const actions = [];
|
|
3643
|
-
if (isPresentation) {
|
|
3644
|
-
|
|
3645
|
-
{
|
|
4096
|
+
if (isPresentation || totalPages > 1) {
|
|
4097
|
+
if (isPresentation) {
|
|
4098
|
+
actions.push({
|
|
3646
4099
|
id: "thumbnails",
|
|
3647
4100
|
icon: "thumbnails",
|
|
3648
4101
|
label: "Slide Thumbnails",
|
|
3649
4102
|
type: "button",
|
|
3650
4103
|
group: "navigation",
|
|
3651
4104
|
execute: () => instance.toggleThumbnails?.()
|
|
3652
|
-
}
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3670
|
-
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
}
|
|
4105
|
+
});
|
|
4106
|
+
}
|
|
4107
|
+
actions.push({
|
|
4108
|
+
id: "page-nav",
|
|
4109
|
+
icon: "",
|
|
4110
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
4111
|
+
type: "page-nav",
|
|
4112
|
+
group: "navigation",
|
|
4113
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4114
|
+
max: totalPages,
|
|
4115
|
+
execute: (action, page) => {
|
|
4116
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4117
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4118
|
+
if (action === "prev") {
|
|
4119
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4120
|
+
} else if (action === "next") {
|
|
4121
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4122
|
+
} else if (typeof page === "number") {
|
|
4123
|
+
instance.goToPage?.(page);
|
|
4124
|
+
} else if (typeof action === "number") {
|
|
4125
|
+
instance.goToPage?.(action);
|
|
3674
4126
|
}
|
|
3675
4127
|
}
|
|
3676
|
-
);
|
|
4128
|
+
});
|
|
3677
4129
|
}
|
|
3678
4130
|
actions.push(
|
|
3679
4131
|
{
|
|
@@ -4050,10 +4502,37 @@ var DocPlugin = class {
|
|
|
4050
4502
|
supports(file) {
|
|
4051
4503
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4052
4504
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4053
|
-
|
|
4505
|
+
if (ext) {
|
|
4506
|
+
return this.extensions.includes(ext);
|
|
4507
|
+
}
|
|
4508
|
+
return this.mimeTypes.includes(mime || "");
|
|
4054
4509
|
}
|
|
4055
4510
|
getToolbarActions(instance) {
|
|
4056
|
-
|
|
4511
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4512
|
+
const actions = [];
|
|
4513
|
+
if (totalPages > 1) {
|
|
4514
|
+
actions.push({
|
|
4515
|
+
id: "page-nav",
|
|
4516
|
+
icon: "",
|
|
4517
|
+
label: "Page Navigation",
|
|
4518
|
+
type: "page-nav",
|
|
4519
|
+
group: "navigation",
|
|
4520
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4521
|
+
max: totalPages,
|
|
4522
|
+
execute: (action, page) => {
|
|
4523
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4524
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4525
|
+
if (action === "prev") {
|
|
4526
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4527
|
+
} else if (action === "next") {
|
|
4528
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4529
|
+
} else if (typeof page === "number") {
|
|
4530
|
+
instance.goToPage?.(page);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
});
|
|
4534
|
+
}
|
|
4535
|
+
actions.push(
|
|
4057
4536
|
{
|
|
4058
4537
|
id: "zoom-out",
|
|
4059
4538
|
icon: "zoom-out",
|
|
@@ -4102,7 +4581,8 @@ var DocPlugin = class {
|
|
|
4102
4581
|
group: "actions",
|
|
4103
4582
|
execute: () => instance.print?.()
|
|
4104
4583
|
}
|
|
4105
|
-
|
|
4584
|
+
);
|
|
4585
|
+
return actions;
|
|
4106
4586
|
}
|
|
4107
4587
|
async render(ctx) {
|
|
4108
4588
|
const container = document.createElement("div");
|
|
@@ -4129,6 +4609,7 @@ var DocPlugin = class {
|
|
|
4129
4609
|
ctx.container.appendChild(container);
|
|
4130
4610
|
let scale = 1;
|
|
4131
4611
|
let extractedRawText = "";
|
|
4612
|
+
let isFallback = false;
|
|
4132
4613
|
try {
|
|
4133
4614
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4134
4615
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
@@ -4142,26 +4623,89 @@ var DocPlugin = class {
|
|
|
4142
4623
|
const tableStream = cfbf.readStream(tableName);
|
|
4143
4624
|
const text = this.extractDocText(wordDocStream, tableStream);
|
|
4144
4625
|
extractedRawText = text;
|
|
4145
|
-
wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
|
|
4146
4626
|
} catch (err) {
|
|
4147
4627
|
console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
|
|
4148
4628
|
const fallback = this.heuristicTextExtraction(ctx.buffer);
|
|
4149
4629
|
extractedRawText = fallback;
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4630
|
+
isFallback = true;
|
|
4631
|
+
}
|
|
4632
|
+
const rawPages = this.splitIntoPages(extractedRawText);
|
|
4633
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
4634
|
+
let currentPage = 1;
|
|
4635
|
+
wrapper.innerHTML = "";
|
|
4636
|
+
const pageCards = [];
|
|
4637
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4638
|
+
const pageCard = document.createElement("div");
|
|
4639
|
+
pageCard.className = "fp-doc-page-card";
|
|
4640
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
4641
|
+
pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
|
|
4642
|
+
pageCard.style.borderRadius = "4px";
|
|
4643
|
+
pageCard.style.padding = "56px 48px";
|
|
4644
|
+
pageCard.style.minHeight = "100%";
|
|
4645
|
+
pageCard.style.display = i === 0 ? "block" : "none";
|
|
4646
|
+
if (isFallback && i === 0) {
|
|
4647
|
+
pageCard.innerHTML = `
|
|
4648
|
+
<div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
|
|
4649
|
+
<h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
|
|
4650
|
+
<span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
|
|
4651
|
+
</div>
|
|
4652
|
+
${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
|
|
4653
|
+
`;
|
|
4654
|
+
} else {
|
|
4655
|
+
pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
|
|
4656
|
+
}
|
|
4657
|
+
wrapper.appendChild(pageCard);
|
|
4658
|
+
pageCards.push(pageCard);
|
|
4659
|
+
}
|
|
4660
|
+
let indicator = null;
|
|
4661
|
+
if (totalPages > 1) {
|
|
4662
|
+
indicator = document.createElement("div");
|
|
4663
|
+
indicator.className = "fp-doc-page-indicator";
|
|
4664
|
+
indicator.style.position = "sticky";
|
|
4665
|
+
indicator.style.bottom = "16px";
|
|
4666
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4667
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4668
|
+
indicator.style.color = "#f8fafc";
|
|
4669
|
+
indicator.style.fontSize = "12px";
|
|
4670
|
+
indicator.style.fontWeight = "600";
|
|
4671
|
+
indicator.style.padding = "5px 14px";
|
|
4672
|
+
indicator.style.borderRadius = "20px";
|
|
4673
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4674
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4675
|
+
indicator.style.zIndex = "10";
|
|
4676
|
+
indicator.style.userSelect = "none";
|
|
4677
|
+
indicator.style.pointerEvents = "none";
|
|
4678
|
+
indicator.style.textAlign = "center";
|
|
4679
|
+
indicator.style.width = "fit-content";
|
|
4680
|
+
indicator.style.margin = "16px auto 0";
|
|
4681
|
+
container.appendChild(indicator);
|
|
4682
|
+
}
|
|
4683
|
+
const showPage = (pageNum) => {
|
|
4684
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4685
|
+
if (totalPages > 1) {
|
|
4686
|
+
pageCards.forEach((card, idx) => {
|
|
4687
|
+
card.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4688
|
+
});
|
|
4689
|
+
}
|
|
4690
|
+
if (indicator) {
|
|
4691
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4692
|
+
}
|
|
4693
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4694
|
+
};
|
|
4695
|
+
if (totalPages > 1) {
|
|
4696
|
+
showPage(1);
|
|
4157
4697
|
}
|
|
4158
4698
|
const cleanup = () => {
|
|
4699
|
+
indicator?.remove();
|
|
4159
4700
|
container.remove();
|
|
4160
4701
|
ctx.container.innerHTML = "";
|
|
4161
4702
|
};
|
|
4162
4703
|
ctx.signal.addEventListener("abort", cleanup);
|
|
4163
4704
|
return {
|
|
4164
4705
|
destroy: cleanup,
|
|
4706
|
+
getPageCount: () => totalPages,
|
|
4707
|
+
getCurrentPage: () => currentPage,
|
|
4708
|
+
goToPage: (page) => showPage(page),
|
|
4165
4709
|
zoomIn: () => {
|
|
4166
4710
|
scale += 0.1;
|
|
4167
4711
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -4273,31 +4817,36 @@ var DocPlugin = class {
|
|
|
4273
4817
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4274
4818
|
*/
|
|
4275
4819
|
extractStringsFromBytes(bytes) {
|
|
4276
|
-
const
|
|
4277
|
-
const
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4820
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4821
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4822
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4823
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4824
|
+
const candidateLines = [];
|
|
4825
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4826
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4827
|
+
const trimmed = run.trim();
|
|
4828
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4829
|
+
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)) {
|
|
4830
|
+
seen.add(trimmed);
|
|
4831
|
+
candidateLines.push(trimmed);
|
|
4832
|
+
}
|
|
4289
4833
|
}
|
|
4290
4834
|
}
|
|
4291
|
-
return
|
|
4835
|
+
return candidateLines.join("\n\n");
|
|
4292
4836
|
}
|
|
4293
4837
|
heuristicTextExtraction(buffer) {
|
|
4294
4838
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
4295
4839
|
}
|
|
4840
|
+
splitIntoPages(text) {
|
|
4841
|
+
if (!text) return [""];
|
|
4842
|
+
const parts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
4843
|
+
return parts.length > 0 ? parts : [text];
|
|
4844
|
+
}
|
|
4296
4845
|
/**
|
|
4297
4846
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
4298
4847
|
*/
|
|
4299
4848
|
formatDocToHtml(text, filename) {
|
|
4300
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").
|
|
4849
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
4301
4850
|
let html = "";
|
|
4302
4851
|
let inList = false;
|
|
4303
4852
|
for (const rawLine of lines) {
|
|
@@ -4343,14 +4892,17 @@ function docPlugin() {
|
|
|
4343
4892
|
}
|
|
4344
4893
|
var PptPlugin = class {
|
|
4345
4894
|
id = "ppt";
|
|
4346
|
-
name = "
|
|
4895
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4347
4896
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4348
4897
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4349
|
-
weight =
|
|
4898
|
+
weight = 85;
|
|
4350
4899
|
supports(file) {
|
|
4351
4900
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4352
4901
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4353
|
-
|
|
4902
|
+
if (ext) {
|
|
4903
|
+
return this.extensions.includes(ext);
|
|
4904
|
+
}
|
|
4905
|
+
return this.mimeTypes.includes(mime || "");
|
|
4354
4906
|
}
|
|
4355
4907
|
getToolbarActions(instance) {
|
|
4356
4908
|
return [
|
|
@@ -4438,19 +4990,15 @@ var PptPlugin = class {
|
|
|
4438
4990
|
container.style.alignItems = "center";
|
|
4439
4991
|
container.style.padding = "32px 16px";
|
|
4440
4992
|
container.style.backgroundColor = "#0f172a";
|
|
4993
|
+
container.style.boxSizing = "border-box";
|
|
4441
4994
|
const slideCard = document.createElement("div");
|
|
4442
4995
|
slideCard.className = "fp-ppt-slide-card";
|
|
4443
4996
|
slideCard.style.width = "960px";
|
|
4444
|
-
slideCard.style.maxWidth = "
|
|
4997
|
+
slideCard.style.maxWidth = "92%";
|
|
4445
4998
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4446
4999
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4447
|
-
slideCard.style.boxShadow = "0
|
|
5000
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4448
5001
|
slideCard.style.borderRadius = "8px";
|
|
4449
|
-
slideCard.style.padding = "48px";
|
|
4450
|
-
slideCard.style.display = "flex";
|
|
4451
|
-
slideCard.style.flexDirection = "column";
|
|
4452
|
-
slideCard.style.justifyContent = "center";
|
|
4453
|
-
slideCard.style.alignItems = "center";
|
|
4454
5002
|
slideCard.style.boxSizing = "border-box";
|
|
4455
5003
|
slideCard.style.position = "relative";
|
|
4456
5004
|
slideCard.style.overflow = "hidden";
|
|
@@ -4461,21 +5009,25 @@ var PptPlugin = class {
|
|
|
4461
5009
|
let scale = 1;
|
|
4462
5010
|
let currentSlide = 1;
|
|
4463
5011
|
let slides = [];
|
|
5012
|
+
const createdBlobUrls = [];
|
|
4464
5013
|
try {
|
|
4465
5014
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4466
5015
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4467
5016
|
if (!pptStream || pptStream.length < 512) {
|
|
4468
5017
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4469
5018
|
}
|
|
4470
|
-
|
|
5019
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
5020
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4471
5021
|
} catch (err) {
|
|
4472
5022
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4473
5023
|
}
|
|
4474
5024
|
if (slides.length === 0) {
|
|
4475
5025
|
slides = [
|
|
4476
5026
|
{
|
|
5027
|
+
slideIndex: 1,
|
|
4477
5028
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4478
|
-
|
|
5029
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
5030
|
+
tableColumns: []
|
|
4479
5031
|
}
|
|
4480
5032
|
];
|
|
4481
5033
|
}
|
|
@@ -4484,23 +5036,73 @@ var PptPlugin = class {
|
|
|
4484
5036
|
currentSlide = idx;
|
|
4485
5037
|
const s = slides[idx - 1];
|
|
4486
5038
|
if (!s) return;
|
|
5039
|
+
let contentHtml = "";
|
|
5040
|
+
if (s.pictureUrl) {
|
|
5041
|
+
contentHtml = `
|
|
5042
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
5043
|
+
<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;" />
|
|
5044
|
+
</div>
|
|
5045
|
+
`;
|
|
5046
|
+
} else if (s.tableColumns.length > 0) {
|
|
5047
|
+
const cols = s.tableColumns;
|
|
5048
|
+
contentHtml = `
|
|
5049
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
5050
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
5051
|
+
<thead>
|
|
5052
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
5053
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
5054
|
+
</tr>
|
|
5055
|
+
</thead>
|
|
5056
|
+
<tbody>
|
|
5057
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
5058
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
5059
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
5060
|
+
</tr>
|
|
5061
|
+
`).join("")}
|
|
5062
|
+
</tbody>
|
|
5063
|
+
</table>
|
|
5064
|
+
</div>
|
|
5065
|
+
`;
|
|
5066
|
+
} else {
|
|
5067
|
+
const pTags = s.paragraphs.map((p) => {
|
|
5068
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
5069
|
+
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("");
|
|
5070
|
+
}).join("");
|
|
5071
|
+
contentHtml = `
|
|
5072
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
5073
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
5074
|
+
</div>
|
|
5075
|
+
`;
|
|
5076
|
+
}
|
|
4487
5077
|
slideCard.innerHTML = `
|
|
4488
|
-
<div style="
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
5078
|
+
<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;">
|
|
5079
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
5080
|
+
<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;">
|
|
5081
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
5082
|
+
${DOMPurify2.sanitize(s.title)}
|
|
5083
|
+
</h1>
|
|
5084
|
+
${s.subtitle ? `
|
|
5085
|
+
<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);">
|
|
5086
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
5087
|
+
</span>
|
|
5088
|
+
` : `
|
|
5089
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
5090
|
+
Slide ${idx} / ${totalSlides}
|
|
5091
|
+
</span>
|
|
5092
|
+
`}
|
|
4497
5093
|
</div>
|
|
5094
|
+
|
|
5095
|
+
<!-- Slide Content -->
|
|
5096
|
+
${contentHtml}
|
|
4498
5097
|
</div>
|
|
4499
5098
|
`;
|
|
4500
5099
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4501
5100
|
};
|
|
4502
5101
|
renderSlide(1);
|
|
4503
5102
|
const cleanup = () => {
|
|
5103
|
+
for (const u of createdBlobUrls) {
|
|
5104
|
+
URL.revokeObjectURL(u);
|
|
5105
|
+
}
|
|
4504
5106
|
container.remove();
|
|
4505
5107
|
ctx.container.innerHTML = "";
|
|
4506
5108
|
};
|
|
@@ -4540,13 +5142,48 @@ var PptPlugin = class {
|
|
|
4540
5142
|
if (!ctx2d) return;
|
|
4541
5143
|
canvas.width = 160;
|
|
4542
5144
|
canvas.height = 90;
|
|
4543
|
-
ctx2d.fillStyle = "#
|
|
5145
|
+
ctx2d.fillStyle = "#334155";
|
|
4544
5146
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4545
|
-
ctx2d.fillStyle = "#
|
|
4546
|
-
ctx2d.
|
|
4547
|
-
ctx2d.
|
|
4548
|
-
|
|
4549
|
-
ctx2d.
|
|
5147
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5148
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
5149
|
+
ctx2d.fillStyle = "#84cc16";
|
|
5150
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
5151
|
+
ctx2d.fillStyle = "#1e293b";
|
|
5152
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
5153
|
+
ctx2d.textAlign = "left";
|
|
5154
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
5155
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
5156
|
+
if (s.subtitle) {
|
|
5157
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
5158
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
5159
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5160
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
5161
|
+
ctx2d.textAlign = "center";
|
|
5162
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
5163
|
+
}
|
|
5164
|
+
if (s.pictureUrl) {
|
|
5165
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
5166
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
5167
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5168
|
+
ctx2d.font = "8px sans-serif";
|
|
5169
|
+
ctx2d.textAlign = "center";
|
|
5170
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
5171
|
+
} else if (s.tableColumns.length > 0) {
|
|
5172
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
5173
|
+
ctx2d.lineWidth = 1;
|
|
5174
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
5175
|
+
for (let l = 1; l <= 3; l++) {
|
|
5176
|
+
ctx2d.beginPath();
|
|
5177
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
5178
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
5179
|
+
ctx2d.stroke();
|
|
5180
|
+
}
|
|
5181
|
+
} else {
|
|
5182
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
5183
|
+
for (let l = 0; l < 4; l++) {
|
|
5184
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
5185
|
+
}
|
|
5186
|
+
}
|
|
4550
5187
|
}
|
|
4551
5188
|
}));
|
|
4552
5189
|
},
|
|
@@ -4564,66 +5201,165 @@ var PptPlugin = class {
|
|
|
4564
5201
|
}
|
|
4565
5202
|
};
|
|
4566
5203
|
}
|
|
5204
|
+
/**
|
|
5205
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
5206
|
+
*/
|
|
5207
|
+
extractPictures(cfbf, createdUrls) {
|
|
5208
|
+
const urls = [];
|
|
5209
|
+
try {
|
|
5210
|
+
const picStream = cfbf.readStream("Pictures");
|
|
5211
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
5212
|
+
const pBuf = new Uint8Array(picStream);
|
|
5213
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
5214
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
5215
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
5216
|
+
let match = true;
|
|
5217
|
+
for (let j = 0; j < 8; j++) {
|
|
5218
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
5219
|
+
match = false;
|
|
5220
|
+
break;
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
if (match) {
|
|
5224
|
+
let endIdx = -1;
|
|
5225
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
5226
|
+
let endMatch = true;
|
|
5227
|
+
for (let j = 0; j < 8; j++) {
|
|
5228
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
5229
|
+
endMatch = false;
|
|
5230
|
+
break;
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
if (endMatch) {
|
|
5234
|
+
endIdx = k + 8;
|
|
5235
|
+
break;
|
|
5236
|
+
}
|
|
5237
|
+
}
|
|
5238
|
+
if (endIdx !== -1) {
|
|
5239
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
5240
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
5241
|
+
const url = URL.createObjectURL(blob);
|
|
5242
|
+
urls.push(url);
|
|
5243
|
+
createdUrls.push(url);
|
|
5244
|
+
i = endIdx;
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
}
|
|
5248
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
5249
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
5250
|
+
let endIdx = -1;
|
|
5251
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
5252
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
5253
|
+
endIdx = k + 2;
|
|
5254
|
+
break;
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5257
|
+
if (endIdx !== -1) {
|
|
5258
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
5259
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
5260
|
+
const url = URL.createObjectURL(blob);
|
|
5261
|
+
urls.push(url);
|
|
5262
|
+
createdUrls.push(url);
|
|
5263
|
+
i = endIdx;
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
} catch (err) {
|
|
5268
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
5269
|
+
}
|
|
5270
|
+
return urls;
|
|
5271
|
+
}
|
|
4567
5272
|
/**
|
|
4568
5273
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4569
5274
|
*/
|
|
4570
|
-
extractSlides(stream) {
|
|
5275
|
+
extractSlides(stream, pictures) {
|
|
4571
5276
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4572
5277
|
const len = stream.length;
|
|
4573
5278
|
let offset = 0;
|
|
4574
5279
|
const slides = [];
|
|
4575
|
-
let
|
|
5280
|
+
let picIdx = 0;
|
|
4576
5281
|
while (offset + 8 <= len) {
|
|
4577
5282
|
const recVerInst = view.getUint16(offset, true);
|
|
4578
5283
|
const recType = view.getUint16(offset + 2, true);
|
|
4579
5284
|
const recLen = view.getUint32(offset + 4, true);
|
|
5285
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4580
5286
|
if (recType === 1006) {
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
5287
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
5288
|
+
const rawTexts = [];
|
|
5289
|
+
let hasOle = false;
|
|
5290
|
+
let sOff = offset + 8;
|
|
5291
|
+
while (sOff + 8 <= slideEnd) {
|
|
5292
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
5293
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
5294
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
5295
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
5296
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
5297
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
5298
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
5299
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
5300
|
+
rawTexts.push(txt);
|
|
5301
|
+
}
|
|
5302
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
5303
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
5304
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
5305
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
5306
|
+
rawTexts.push(txt);
|
|
5307
|
+
}
|
|
5308
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
5309
|
+
hasOle = true;
|
|
5310
|
+
}
|
|
5311
|
+
if (cIsContainer) sOff += 8;
|
|
5312
|
+
else sOff += 8 + cLen;
|
|
4586
5313
|
}
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
5314
|
+
let title = "";
|
|
5315
|
+
let subtitle = "";
|
|
5316
|
+
const paragraphs = [];
|
|
5317
|
+
const tableColumns = [];
|
|
5318
|
+
for (const t of rawTexts) {
|
|
5319
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
5320
|
+
title = t;
|
|
5321
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
5322
|
+
tableColumns.push(t);
|
|
5323
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
5324
|
+
subtitle = t;
|
|
5325
|
+
} else {
|
|
5326
|
+
paragraphs.push(t);
|
|
5327
|
+
}
|
|
4595
5328
|
}
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4600
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4601
|
-
currentSlideTexts.push(text);
|
|
5329
|
+
let pictureUrl = null;
|
|
5330
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
5331
|
+
pictureUrl = pictures[picIdx++];
|
|
4602
5332
|
}
|
|
5333
|
+
slides.push({
|
|
5334
|
+
slideIndex: slides.length + 1,
|
|
5335
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
5336
|
+
subtitle,
|
|
5337
|
+
paragraphs,
|
|
5338
|
+
tableColumns,
|
|
5339
|
+
pictureUrl,
|
|
5340
|
+
hasOle
|
|
5341
|
+
});
|
|
4603
5342
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
offset += 8;
|
|
4607
|
-
} else {
|
|
4608
|
-
offset += 8 + recLen;
|
|
4609
|
-
}
|
|
4610
|
-
}
|
|
4611
|
-
if (currentSlideTexts.length > 0) {
|
|
4612
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4613
|
-
const texts = currentSlideTexts.slice(1);
|
|
4614
|
-
slides.push({ title, texts });
|
|
5343
|
+
if (isContainer) offset += 8;
|
|
5344
|
+
else offset += 8 + recLen;
|
|
4615
5345
|
}
|
|
4616
5346
|
if (slides.length === 0) {
|
|
4617
5347
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4618
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4619
|
-
const
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
5348
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
5349
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
5350
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
5351
|
+
);
|
|
5352
|
+
if (clean.length > 0) {
|
|
5353
|
+
const chunkSize = 3;
|
|
5354
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
5355
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4624
5356
|
slides.push({
|
|
5357
|
+
slideIndex: slides.length + 1,
|
|
4625
5358
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4626
|
-
|
|
5359
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
5360
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
5361
|
+
tableColumns: [],
|
|
5362
|
+
pictureUrl: pictures[slides.length] || null
|
|
4627
5363
|
});
|
|
4628
5364
|
}
|
|
4629
5365
|
}
|