@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/react.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { memo, forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
|
|
2
2
|
import DOMPurify2 from 'dompurify';
|
|
3
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
3
4
|
import * as docx from 'docx-preview';
|
|
4
5
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
5
6
|
import * as XLSX from 'xlsx';
|
|
@@ -10,7 +11,7 @@ import * as THREE from 'three';
|
|
|
10
11
|
import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
|
|
11
12
|
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
|
12
13
|
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
13
|
-
import
|
|
14
|
+
import * as RTFJS from 'rtf.js/dist/RTFJS.bundle.js';
|
|
14
15
|
import { jsx } from 'react/jsx-runtime';
|
|
15
16
|
|
|
16
17
|
// src/react.tsx
|
|
@@ -267,6 +268,39 @@ function detectOoxmlType(buffer) {
|
|
|
267
268
|
}
|
|
268
269
|
return "application/zip";
|
|
269
270
|
}
|
|
271
|
+
function detectCfbfType(buffer) {
|
|
272
|
+
const bytes = new Uint8Array(buffer);
|
|
273
|
+
const hasUtf16le = (str) => {
|
|
274
|
+
const target = new Uint8Array(str.length * 2);
|
|
275
|
+
for (let i = 0; i < str.length; i++) {
|
|
276
|
+
target[i * 2] = str.charCodeAt(i);
|
|
277
|
+
target[i * 2 + 1] = 0;
|
|
278
|
+
}
|
|
279
|
+
const targetLen = target.length;
|
|
280
|
+
const max = bytes.length - targetLen;
|
|
281
|
+
for (let i = 0; i <= max; i++) {
|
|
282
|
+
let match = true;
|
|
283
|
+
for (let j = 0; j < targetLen; j++) {
|
|
284
|
+
if (bytes[i + j] !== target[j]) {
|
|
285
|
+
match = false;
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (match) return true;
|
|
290
|
+
}
|
|
291
|
+
return false;
|
|
292
|
+
};
|
|
293
|
+
if (hasUtf16le("PowerPoint Document")) {
|
|
294
|
+
return { mime: "application/vnd.ms-powerpoint", extension: ".ppt" };
|
|
295
|
+
}
|
|
296
|
+
if (hasUtf16le("WordDocument")) {
|
|
297
|
+
return { mime: "application/msword", extension: ".doc" };
|
|
298
|
+
}
|
|
299
|
+
if (hasUtf16le("Workbook") || hasUtf16le("Book")) {
|
|
300
|
+
return { mime: "application/vnd.ms-excel", extension: ".xls" };
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
270
304
|
function extractExtension(nameOrUrl) {
|
|
271
305
|
try {
|
|
272
306
|
const url = new URL(nameOrUrl);
|
|
@@ -331,6 +365,18 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
331
365
|
} else {
|
|
332
366
|
metadata.mimeType = metadata.mimeType ?? magicMime;
|
|
333
367
|
}
|
|
368
|
+
} else if (magicMime === "application/x-cfbf") {
|
|
369
|
+
if (metadata.extension) {
|
|
370
|
+
metadata.mimeType = mimeFromExtension(metadata.extension) ?? magicMime;
|
|
371
|
+
} else {
|
|
372
|
+
const cfbf = detectCfbfType(buffer);
|
|
373
|
+
if (cfbf) {
|
|
374
|
+
metadata.mimeType = cfbf.mime;
|
|
375
|
+
metadata.extension = cfbf.extension;
|
|
376
|
+
} else {
|
|
377
|
+
metadata.mimeType = magicMime;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
334
380
|
} else {
|
|
335
381
|
metadata.mimeType = magicMime;
|
|
336
382
|
}
|
|
@@ -435,11 +481,21 @@ var ToolbarController = class {
|
|
|
435
481
|
el;
|
|
436
482
|
toolbarEl;
|
|
437
483
|
actions = [];
|
|
484
|
+
pageInputEl = null;
|
|
485
|
+
pageLabelEl = null;
|
|
438
486
|
constructor(container) {
|
|
439
487
|
this.el = container;
|
|
440
488
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
441
489
|
this.el.appendChild(this.toolbarEl);
|
|
442
490
|
}
|
|
491
|
+
setPage(page, max) {
|
|
492
|
+
if (this.pageInputEl) {
|
|
493
|
+
this.pageInputEl.value = page.toString();
|
|
494
|
+
}
|
|
495
|
+
if (max !== void 0 && this.pageLabelEl) {
|
|
496
|
+
this.pageLabelEl.textContent = ` / ${max}`;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
443
499
|
update(actions) {
|
|
444
500
|
this.actions = actions;
|
|
445
501
|
this.render();
|
|
@@ -482,6 +538,7 @@ var ToolbarController = class {
|
|
|
482
538
|
if (action.type === "separator") {
|
|
483
539
|
groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
|
|
484
540
|
} else if (action.type === "page-nav") {
|
|
541
|
+
const max = action.max ?? 1;
|
|
485
542
|
const prevBtn = this.createButton(
|
|
486
543
|
"prev",
|
|
487
544
|
ICON_PAGE_PREV,
|
|
@@ -500,7 +557,6 @@ var ToolbarController = class {
|
|
|
500
557
|
"Next Page",
|
|
501
558
|
() => {
|
|
502
559
|
const cur = parseInt(input.value, 10) || 1;
|
|
503
|
-
const max = action.max ?? 1;
|
|
504
560
|
if (cur < max) {
|
|
505
561
|
input.value = (cur + 1).toString();
|
|
506
562
|
action.execute("next", cur + 1);
|
|
@@ -512,15 +568,18 @@ var ToolbarController = class {
|
|
|
512
568
|
type: "number",
|
|
513
569
|
value: (action.value ?? 1).toString(),
|
|
514
570
|
min: "1",
|
|
515
|
-
max:
|
|
571
|
+
max: max.toString()
|
|
516
572
|
});
|
|
517
573
|
input.addEventListener("change", () => {
|
|
518
|
-
|
|
519
|
-
if (
|
|
520
|
-
|
|
521
|
-
|
|
574
|
+
let val = parseInt(input.value, 10);
|
|
575
|
+
if (isNaN(val)) val = 1;
|
|
576
|
+
val = Math.max(1, Math.min(max, val));
|
|
577
|
+
input.value = val.toString();
|
|
578
|
+
action.execute("go", val);
|
|
522
579
|
});
|
|
523
|
-
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${
|
|
580
|
+
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
581
|
+
this.pageInputEl = input;
|
|
582
|
+
this.pageLabelEl = label;
|
|
524
583
|
groupEl.appendChild(prevBtn);
|
|
525
584
|
groupEl.appendChild(input);
|
|
526
585
|
groupEl.appendChild(label);
|
|
@@ -698,7 +757,13 @@ var FilePreviewViewer = class {
|
|
|
698
757
|
buffer,
|
|
699
758
|
options,
|
|
700
759
|
signal,
|
|
701
|
-
emit: (event, payload) =>
|
|
760
|
+
emit: (event, payload) => {
|
|
761
|
+
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
762
|
+
const total = payload.total ?? payload.totalPages;
|
|
763
|
+
this.toolbar?.setPage(payload.page, total);
|
|
764
|
+
}
|
|
765
|
+
this.eventEmitter.emit(event, payload);
|
|
766
|
+
}
|
|
702
767
|
});
|
|
703
768
|
this.activeInstance = instance;
|
|
704
769
|
this.hideLoading();
|
|
@@ -732,6 +797,7 @@ var FilePreviewViewer = class {
|
|
|
732
797
|
if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
|
|
733
798
|
this.thumbnailPanel.update(thumbnails, (index) => {
|
|
734
799
|
instance.goToPage?.(index + 1);
|
|
800
|
+
this.toolbar?.setPage(index + 1);
|
|
735
801
|
});
|
|
736
802
|
if (options.showThumbnails) {
|
|
737
803
|
this.thumbnailPanel.show();
|
|
@@ -857,9 +923,13 @@ var FilePreviewViewer = class {
|
|
|
857
923
|
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
858
924
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
859
925
|
this.activeInstance.goToPage?.(cur + 1);
|
|
926
|
+
const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
|
|
927
|
+
this.toolbar?.setPage(nextCur);
|
|
860
928
|
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
861
929
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
862
930
|
this.activeInstance.goToPage?.(Math.max(1, cur - 1));
|
|
931
|
+
const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
|
|
932
|
+
this.toolbar?.setPage(prevCur);
|
|
863
933
|
} else if (e.key === "+" || e.key === "=") {
|
|
864
934
|
this.activeInstance.zoomIn?.();
|
|
865
935
|
} else if (e.key === "-" || e.key === "_") {
|
|
@@ -1125,30 +1195,62 @@ var CfbfReader = class {
|
|
|
1125
1195
|
return result.subarray(0, targetSize);
|
|
1126
1196
|
}
|
|
1127
1197
|
};
|
|
1128
|
-
|
|
1129
|
-
|
|
1198
|
+
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1199
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1200
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1130
1203
|
var PdfPlugin = class {
|
|
1131
1204
|
id = "pdf";
|
|
1132
|
-
name = "PDF Preview";
|
|
1205
|
+
name = "PDF Document Preview";
|
|
1133
1206
|
extensions = [".pdf"];
|
|
1134
1207
|
mimeTypes = ["application/pdf"];
|
|
1135
1208
|
weight = 100;
|
|
1136
1209
|
supports(file) {
|
|
1137
1210
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1138
1211
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1139
|
-
|
|
1212
|
+
if (ext) return this.extensions.includes(ext);
|
|
1213
|
+
return this.mimeTypes.includes(mime || "");
|
|
1140
1214
|
}
|
|
1141
1215
|
getToolbarActions(instance) {
|
|
1216
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1217
|
+
const curPage = instance.getCurrentPage?.() ?? 1;
|
|
1142
1218
|
return [
|
|
1219
|
+
{
|
|
1220
|
+
id: "thumbnails",
|
|
1221
|
+
icon: "thumbnails",
|
|
1222
|
+
label: "Page Thumbnails",
|
|
1223
|
+
type: "button",
|
|
1224
|
+
group: "navigation",
|
|
1225
|
+
execute: () => instance.toggleThumbnails?.()
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
id: "page-nav",
|
|
1229
|
+
icon: "",
|
|
1230
|
+
label: "Page Navigation",
|
|
1231
|
+
type: "page-nav",
|
|
1232
|
+
group: "navigation",
|
|
1233
|
+
value: curPage,
|
|
1234
|
+
max: totalPages,
|
|
1235
|
+
execute: (action, page) => {
|
|
1236
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1237
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1238
|
+
if (action === "prev") {
|
|
1239
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1240
|
+
} else if (action === "next") {
|
|
1241
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1242
|
+
} else if (typeof page === "number") {
|
|
1243
|
+
instance.goToPage?.(page);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1143
1247
|
{
|
|
1144
1248
|
id: "zoom-out",
|
|
1145
1249
|
icon: "zoom-out",
|
|
1146
1250
|
label: "Zoom Out",
|
|
1147
1251
|
type: "button",
|
|
1148
1252
|
group: "zoom",
|
|
1149
|
-
execute: () =>
|
|
1150
|
-
instance.zoomOut?.();
|
|
1151
|
-
}
|
|
1253
|
+
execute: () => instance.zoomOut?.()
|
|
1152
1254
|
},
|
|
1153
1255
|
{
|
|
1154
1256
|
id: "zoom-in",
|
|
@@ -1156,9 +1258,7 @@ var PdfPlugin = class {
|
|
|
1156
1258
|
label: "Zoom In",
|
|
1157
1259
|
type: "button",
|
|
1158
1260
|
group: "zoom",
|
|
1159
|
-
execute: () =>
|
|
1160
|
-
instance.zoomIn?.();
|
|
1161
|
-
}
|
|
1261
|
+
execute: () => instance.zoomIn?.()
|
|
1162
1262
|
},
|
|
1163
1263
|
{
|
|
1164
1264
|
id: "fit-page",
|
|
@@ -1166,39 +1266,23 @@ var PdfPlugin = class {
|
|
|
1166
1266
|
label: "Fit to Page",
|
|
1167
1267
|
type: "button",
|
|
1168
1268
|
group: "zoom",
|
|
1169
|
-
execute: () =>
|
|
1170
|
-
instance.fitToPage?.();
|
|
1171
|
-
}
|
|
1269
|
+
execute: () => instance.fitToPage?.()
|
|
1172
1270
|
},
|
|
1173
1271
|
{
|
|
1174
1272
|
id: "rotate-cw",
|
|
1175
1273
|
icon: "rotate-cw",
|
|
1176
|
-
label: "Rotate",
|
|
1274
|
+
label: "Rotate Clockwise",
|
|
1177
1275
|
type: "button",
|
|
1178
1276
|
group: "view",
|
|
1179
|
-
execute: () =>
|
|
1180
|
-
instance.rotateCW?.();
|
|
1181
|
-
}
|
|
1182
|
-
},
|
|
1183
|
-
{
|
|
1184
|
-
id: "page-nav",
|
|
1185
|
-
icon: "page-nav",
|
|
1186
|
-
label: "Page Navigation",
|
|
1187
|
-
type: "page-nav",
|
|
1188
|
-
group: "navigation",
|
|
1189
|
-
execute: (page) => {
|
|
1190
|
-
if (typeof page === "number") instance.goToPage?.(page);
|
|
1191
|
-
}
|
|
1277
|
+
execute: () => instance.rotateCW?.()
|
|
1192
1278
|
},
|
|
1193
1279
|
{
|
|
1194
1280
|
id: "download",
|
|
1195
1281
|
icon: "download",
|
|
1196
|
-
label: "Download",
|
|
1282
|
+
label: "Download PDF",
|
|
1197
1283
|
type: "button",
|
|
1198
1284
|
group: "actions",
|
|
1199
|
-
execute: () =>
|
|
1200
|
-
instance.download?.();
|
|
1201
|
-
}
|
|
1285
|
+
execute: () => instance.download?.()
|
|
1202
1286
|
},
|
|
1203
1287
|
{
|
|
1204
1288
|
id: "print",
|
|
@@ -1206,99 +1290,213 @@ var PdfPlugin = class {
|
|
|
1206
1290
|
label: "Print",
|
|
1207
1291
|
type: "button",
|
|
1208
1292
|
group: "actions",
|
|
1209
|
-
execute: () =>
|
|
1210
|
-
instance.print?.();
|
|
1211
|
-
}
|
|
1293
|
+
execute: () => instance.print?.()
|
|
1212
1294
|
}
|
|
1213
1295
|
];
|
|
1214
1296
|
}
|
|
1215
1297
|
async render(ctx) {
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1298
|
+
const container = document.createElement("div");
|
|
1299
|
+
container.className = "fp-pdf-container";
|
|
1300
|
+
container.style.width = "100%";
|
|
1301
|
+
container.style.height = "100%";
|
|
1302
|
+
container.style.overflow = "auto";
|
|
1303
|
+
container.style.display = "flex";
|
|
1304
|
+
container.style.flexDirection = "column";
|
|
1305
|
+
container.style.alignItems = "center";
|
|
1306
|
+
container.style.padding = "24px 16px";
|
|
1307
|
+
container.style.backgroundColor = "#0f172a";
|
|
1308
|
+
container.style.boxSizing = "border-box";
|
|
1309
|
+
container.style.position = "relative";
|
|
1310
|
+
const pageCard = document.createElement("div");
|
|
1311
|
+
pageCard.className = "fp-pdf-page-card";
|
|
1312
|
+
pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
|
|
1313
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
1314
|
+
pageCard.style.borderRadius = "4px";
|
|
1315
|
+
pageCard.style.overflow = "hidden";
|
|
1316
|
+
pageCard.style.lineHeight = "0";
|
|
1317
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
1318
|
+
pageCard.style.position = "relative";
|
|
1319
|
+
const canvas = document.createElement("canvas");
|
|
1320
|
+
pageCard.appendChild(canvas);
|
|
1321
|
+
container.appendChild(pageCard);
|
|
1322
|
+
const indicator = document.createElement("div");
|
|
1323
|
+
indicator.className = "fp-pdf-page-indicator";
|
|
1324
|
+
indicator.style.position = "sticky";
|
|
1325
|
+
indicator.style.bottom = "16px";
|
|
1326
|
+
indicator.style.marginTop = "16px";
|
|
1327
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1328
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1329
|
+
indicator.style.color = "#f8fafc";
|
|
1330
|
+
indicator.style.fontSize = "12px";
|
|
1331
|
+
indicator.style.fontWeight = "600";
|
|
1332
|
+
indicator.style.padding = "5px 14px";
|
|
1333
|
+
indicator.style.borderRadius = "20px";
|
|
1334
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1335
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1336
|
+
indicator.style.zIndex = "10";
|
|
1337
|
+
indicator.style.userSelect = "none";
|
|
1338
|
+
indicator.style.pointerEvents = "none";
|
|
1339
|
+
container.appendChild(indicator);
|
|
1340
|
+
ctx.container.appendChild(container);
|
|
1341
|
+
const loadingTask = pdfjsLib.getDocument({
|
|
1342
|
+
data: new Uint8Array(ctx.buffer),
|
|
1343
|
+
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
|
|
1344
|
+
cMapPacked: true,
|
|
1345
|
+
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
|
|
1346
|
+
});
|
|
1347
|
+
const pdfDoc = await loadingTask.promise;
|
|
1348
|
+
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1232
1349
|
let currentPage = 1;
|
|
1233
|
-
let
|
|
1350
|
+
let zoomScale = 1;
|
|
1234
1351
|
let rotation = 0;
|
|
1352
|
+
let currentRenderTask = null;
|
|
1353
|
+
const renderPage = async (pageNum) => {
|
|
1354
|
+
if (currentRenderTask) {
|
|
1355
|
+
try {
|
|
1356
|
+
currentRenderTask.cancel();
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
currentRenderTask = null;
|
|
1360
|
+
}
|
|
1361
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1362
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1363
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1364
|
+
const page = await pdfDoc.getPage(currentPage);
|
|
1365
|
+
const containerWidth = container.clientWidth || 900;
|
|
1366
|
+
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1367
|
+
const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
|
|
1368
|
+
const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
|
|
1369
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
1370
|
+
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
1371
|
+
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1372
|
+
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1373
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
1374
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
1375
|
+
const canvasCtx = canvas.getContext("2d");
|
|
1376
|
+
if (!canvasCtx) return;
|
|
1377
|
+
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
1378
|
+
currentRenderTask = page.render({
|
|
1379
|
+
canvasContext: canvasCtx,
|
|
1380
|
+
viewport
|
|
1381
|
+
});
|
|
1382
|
+
try {
|
|
1383
|
+
await currentRenderTask.promise;
|
|
1384
|
+
} catch (err) {
|
|
1385
|
+
if (err?.name !== "RenderingCancelledException") {
|
|
1386
|
+
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1387
|
+
}
|
|
1388
|
+
} finally {
|
|
1389
|
+
currentRenderTask = null;
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
await renderPage(1);
|
|
1235
1393
|
const cleanup = () => {
|
|
1236
|
-
|
|
1237
|
-
|
|
1394
|
+
if (currentRenderTask) {
|
|
1395
|
+
try {
|
|
1396
|
+
currentRenderTask.cancel();
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
try {
|
|
1401
|
+
pdfDoc.destroy();
|
|
1402
|
+
} catch {
|
|
1403
|
+
}
|
|
1404
|
+
container.remove();
|
|
1238
1405
|
ctx.container.innerHTML = "";
|
|
1239
1406
|
};
|
|
1240
1407
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1241
|
-
|
|
1408
|
+
const instance = {
|
|
1242
1409
|
destroy: cleanup,
|
|
1243
1410
|
zoomIn: () => {
|
|
1244
|
-
|
|
1245
|
-
|
|
1411
|
+
zoomScale = Math.min(3.5, zoomScale + 0.2);
|
|
1412
|
+
renderPage(currentPage);
|
|
1246
1413
|
},
|
|
1247
1414
|
zoomOut: () => {
|
|
1248
|
-
|
|
1249
|
-
|
|
1415
|
+
zoomScale = Math.max(0.3, zoomScale - 0.2);
|
|
1416
|
+
renderPage(currentPage);
|
|
1250
1417
|
},
|
|
1251
|
-
getZoom: () =>
|
|
1418
|
+
getZoom: () => zoomScale,
|
|
1252
1419
|
setZoom: (level) => {
|
|
1253
|
-
|
|
1254
|
-
|
|
1420
|
+
zoomScale = Math.max(0.3, Math.min(3.5, level));
|
|
1421
|
+
renderPage(currentPage);
|
|
1255
1422
|
},
|
|
1256
1423
|
fitToPage: () => {
|
|
1257
|
-
|
|
1258
|
-
|
|
1424
|
+
zoomScale = 1;
|
|
1425
|
+
renderPage(currentPage);
|
|
1259
1426
|
},
|
|
1260
1427
|
rotateCW: () => {
|
|
1261
1428
|
rotation = (rotation + 90) % 360;
|
|
1262
|
-
|
|
1429
|
+
renderPage(currentPage);
|
|
1263
1430
|
},
|
|
1264
1431
|
rotateCCW: () => {
|
|
1265
1432
|
rotation = (rotation - 90 + 360) % 360;
|
|
1266
|
-
|
|
1433
|
+
renderPage(currentPage);
|
|
1267
1434
|
},
|
|
1268
1435
|
getRotation: () => rotation,
|
|
1436
|
+
getPageCount: () => totalPages,
|
|
1437
|
+
getCurrentPage: () => currentPage,
|
|
1269
1438
|
goToPage: (page) => {
|
|
1270
|
-
|
|
1271
|
-
iframe.src = `${url}#page=${page}`;
|
|
1439
|
+
renderPage(page);
|
|
1272
1440
|
},
|
|
1273
|
-
getCurrentPage: () => currentPage,
|
|
1274
1441
|
download: () => {
|
|
1442
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1443
|
+
const url = URL.createObjectURL(blob);
|
|
1275
1444
|
const a = document.createElement("a");
|
|
1276
1445
|
a.href = url;
|
|
1277
1446
|
a.download = ctx.metadata.name || "document.pdf";
|
|
1278
1447
|
a.click();
|
|
1448
|
+
URL.revokeObjectURL(url);
|
|
1279
1449
|
},
|
|
1280
1450
|
print: () => {
|
|
1281
|
-
|
|
1451
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1452
|
+
const url = URL.createObjectURL(blob);
|
|
1453
|
+
const hiddenIframe = document.createElement("iframe");
|
|
1454
|
+
hiddenIframe.style.position = "fixed";
|
|
1455
|
+
hiddenIframe.style.right = "0";
|
|
1456
|
+
hiddenIframe.style.bottom = "0";
|
|
1457
|
+
hiddenIframe.style.width = "0";
|
|
1458
|
+
hiddenIframe.style.height = "0";
|
|
1459
|
+
hiddenIframe.style.border = "0";
|
|
1460
|
+
document.body.appendChild(hiddenIframe);
|
|
1461
|
+
hiddenIframe.src = url;
|
|
1462
|
+
hiddenIframe.onload = () => {
|
|
1463
|
+
setTimeout(() => {
|
|
1464
|
+
hiddenIframe.contentWindow?.print();
|
|
1465
|
+
setTimeout(() => {
|
|
1466
|
+
hiddenIframe.remove();
|
|
1467
|
+
URL.revokeObjectURL(url);
|
|
1468
|
+
}, 1e3);
|
|
1469
|
+
}, 300);
|
|
1470
|
+
};
|
|
1282
1471
|
},
|
|
1283
1472
|
getThumbnails: async () => {
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1473
|
+
const thumbnails = [];
|
|
1474
|
+
const count = Math.min(totalPages, 50);
|
|
1475
|
+
for (let i = 1; i <= count; i++) {
|
|
1476
|
+
thumbnails.push({
|
|
1477
|
+
index: i,
|
|
1478
|
+
label: `Page ${i}`,
|
|
1479
|
+
render: async (thumbCanvas) => {
|
|
1480
|
+
try {
|
|
1481
|
+
const p = await pdfDoc.getPage(i);
|
|
1482
|
+
const baseVp = p.getViewport({ scale: 1 });
|
|
1483
|
+
const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
|
|
1484
|
+
const thumbVp = p.getViewport({ scale: thumbScale });
|
|
1485
|
+
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1486
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
1487
|
+
if (tCtx) {
|
|
1488
|
+
await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
|
|
1489
|
+
}
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
|
|
1296
1492
|
}
|
|
1297
1493
|
}
|
|
1298
|
-
}
|
|
1299
|
-
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
return thumbnails;
|
|
1300
1497
|
}
|
|
1301
1498
|
};
|
|
1499
|
+
return instance;
|
|
1302
1500
|
}
|
|
1303
1501
|
};
|
|
1304
1502
|
function pdfPlugin() {
|
|
@@ -1619,10 +1817,37 @@ var DocxPlugin = class {
|
|
|
1619
1817
|
supports(file) {
|
|
1620
1818
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1621
1819
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1622
|
-
|
|
1820
|
+
if (ext) {
|
|
1821
|
+
return this.extensions.includes(ext);
|
|
1822
|
+
}
|
|
1823
|
+
return this.mimeTypes.includes(mime || "");
|
|
1623
1824
|
}
|
|
1624
1825
|
getToolbarActions(instance) {
|
|
1625
|
-
|
|
1826
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1827
|
+
const actions = [];
|
|
1828
|
+
if (totalPages > 1) {
|
|
1829
|
+
actions.push({
|
|
1830
|
+
id: "page-nav",
|
|
1831
|
+
icon: "",
|
|
1832
|
+
label: "Page Navigation",
|
|
1833
|
+
type: "page-nav",
|
|
1834
|
+
group: "navigation",
|
|
1835
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
1836
|
+
max: totalPages,
|
|
1837
|
+
execute: (action, page) => {
|
|
1838
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1839
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1840
|
+
if (action === "prev") {
|
|
1841
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1842
|
+
} else if (action === "next") {
|
|
1843
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1844
|
+
} else if (typeof page === "number") {
|
|
1845
|
+
instance.goToPage?.(page);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
actions.push(
|
|
1626
1851
|
{
|
|
1627
1852
|
id: "zoom-out",
|
|
1628
1853
|
icon: "zoom-out",
|
|
@@ -1663,7 +1888,8 @@ var DocxPlugin = class {
|
|
|
1663
1888
|
group: "actions",
|
|
1664
1889
|
execute: () => instance.print?.()
|
|
1665
1890
|
}
|
|
1666
|
-
|
|
1891
|
+
);
|
|
1892
|
+
return actions;
|
|
1667
1893
|
}
|
|
1668
1894
|
async render(ctx) {
|
|
1669
1895
|
const wrapper = document.createElement("div");
|
|
@@ -1739,7 +1965,51 @@ var DocxPlugin = class {
|
|
|
1739
1965
|
}
|
|
1740
1966
|
}
|
|
1741
1967
|
}
|
|
1968
|
+
const sections = wrapper.querySelectorAll("section.docx");
|
|
1969
|
+
const cards = wrapper.querySelectorAll(".fp-docx-page-card");
|
|
1970
|
+
const pageElements = sections.length > 0 ? sections : cards;
|
|
1971
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
1972
|
+
let currentPage = 1;
|
|
1973
|
+
let indicator = null;
|
|
1974
|
+
if (totalPages > 1) {
|
|
1975
|
+
indicator = document.createElement("div");
|
|
1976
|
+
indicator.className = "fp-docx-page-indicator";
|
|
1977
|
+
indicator.style.position = "sticky";
|
|
1978
|
+
indicator.style.bottom = "16px";
|
|
1979
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1980
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1981
|
+
indicator.style.color = "#f8fafc";
|
|
1982
|
+
indicator.style.fontSize = "12px";
|
|
1983
|
+
indicator.style.fontWeight = "600";
|
|
1984
|
+
indicator.style.padding = "5px 14px";
|
|
1985
|
+
indicator.style.borderRadius = "20px";
|
|
1986
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1987
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1988
|
+
indicator.style.zIndex = "10";
|
|
1989
|
+
indicator.style.userSelect = "none";
|
|
1990
|
+
indicator.style.pointerEvents = "none";
|
|
1991
|
+
indicator.style.textAlign = "center";
|
|
1992
|
+
indicator.style.width = "fit-content";
|
|
1993
|
+
indicator.style.margin = "16px auto 0";
|
|
1994
|
+
ctx.container.appendChild(indicator);
|
|
1995
|
+
}
|
|
1996
|
+
const showPage = (pageNum) => {
|
|
1997
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1998
|
+
if (pageElements.length > 1) {
|
|
1999
|
+
pageElements.forEach((sec, idx) => {
|
|
2000
|
+
sec.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
if (indicator) {
|
|
2004
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2005
|
+
}
|
|
2006
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2007
|
+
};
|
|
2008
|
+
if (totalPages > 1) {
|
|
2009
|
+
showPage(1);
|
|
2010
|
+
}
|
|
1742
2011
|
const cleanup = () => {
|
|
2012
|
+
indicator?.remove();
|
|
1743
2013
|
for (const url of createdBlobUrls) {
|
|
1744
2014
|
URL.revokeObjectURL(url);
|
|
1745
2015
|
}
|
|
@@ -1750,6 +2020,11 @@ var DocxPlugin = class {
|
|
|
1750
2020
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1751
2021
|
return {
|
|
1752
2022
|
destroy: cleanup,
|
|
2023
|
+
getPageCount: () => totalPages,
|
|
2024
|
+
getCurrentPage: () => currentPage,
|
|
2025
|
+
goToPage: (page) => {
|
|
2026
|
+
showPage(page);
|
|
2027
|
+
},
|
|
1753
2028
|
zoomIn: () => {
|
|
1754
2029
|
scale += 0.1;
|
|
1755
2030
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -2008,10 +2283,37 @@ var ExcelPlugin = class {
|
|
|
2008
2283
|
supports(file) {
|
|
2009
2284
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2010
2285
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2011
|
-
|
|
2286
|
+
if (ext) {
|
|
2287
|
+
return this.extensions.includes(ext);
|
|
2288
|
+
}
|
|
2289
|
+
return this.mimeTypes.includes(mime || "");
|
|
2012
2290
|
}
|
|
2013
2291
|
getToolbarActions(instance) {
|
|
2014
|
-
|
|
2292
|
+
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2293
|
+
const actions = [];
|
|
2294
|
+
if (totalSheets > 1) {
|
|
2295
|
+
actions.push({
|
|
2296
|
+
id: "page-nav",
|
|
2297
|
+
icon: "",
|
|
2298
|
+
label: "Sheet Navigation",
|
|
2299
|
+
type: "page-nav",
|
|
2300
|
+
group: "navigation",
|
|
2301
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2302
|
+
max: totalSheets,
|
|
2303
|
+
execute: (action, sheet) => {
|
|
2304
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2305
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2306
|
+
if (action === "prev") {
|
|
2307
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2308
|
+
} else if (action === "next") {
|
|
2309
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2310
|
+
} else if (typeof sheet === "number") {
|
|
2311
|
+
instance.goToPage?.(sheet);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
actions.push(
|
|
2015
2317
|
{
|
|
2016
2318
|
id: "zoom-out",
|
|
2017
2319
|
icon: "zoom-out",
|
|
@@ -2032,16 +2334,6 @@ var ExcelPlugin = class {
|
|
|
2032
2334
|
instance.zoomIn?.();
|
|
2033
2335
|
}
|
|
2034
2336
|
},
|
|
2035
|
-
{
|
|
2036
|
-
id: "page-nav",
|
|
2037
|
-
icon: "page-nav",
|
|
2038
|
-
label: "Sheet Navigation",
|
|
2039
|
-
type: "page-nav",
|
|
2040
|
-
group: "navigation",
|
|
2041
|
-
execute: (sheet) => {
|
|
2042
|
-
if (typeof sheet === "number") instance.goToPage?.(sheet);
|
|
2043
|
-
}
|
|
2044
|
-
},
|
|
2045
2337
|
{
|
|
2046
2338
|
id: "download",
|
|
2047
2339
|
icon: "download",
|
|
@@ -2062,7 +2354,8 @@ var ExcelPlugin = class {
|
|
|
2062
2354
|
instance.print?.();
|
|
2063
2355
|
}
|
|
2064
2356
|
}
|
|
2065
|
-
|
|
2357
|
+
);
|
|
2358
|
+
return actions;
|
|
2066
2359
|
}
|
|
2067
2360
|
async render(ctx) {
|
|
2068
2361
|
const container = document.createElement("div");
|
|
@@ -2146,6 +2439,7 @@ var ExcelPlugin = class {
|
|
|
2146
2439
|
b.style.fontWeight = "normal";
|
|
2147
2440
|
}
|
|
2148
2441
|
});
|
|
2442
|
+
ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
|
|
2149
2443
|
};
|
|
2150
2444
|
if (sheetNames.length > 0) {
|
|
2151
2445
|
sheetNames.forEach((name, idx) => {
|
|
@@ -2395,7 +2689,31 @@ var CodePlugin = class {
|
|
|
2395
2689
|
return false;
|
|
2396
2690
|
}
|
|
2397
2691
|
getToolbarActions(instance) {
|
|
2398
|
-
|
|
2692
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2693
|
+
const actions = [];
|
|
2694
|
+
if (totalPages > 1) {
|
|
2695
|
+
actions.push({
|
|
2696
|
+
id: "page-nav",
|
|
2697
|
+
icon: "",
|
|
2698
|
+
label: "Page Navigation",
|
|
2699
|
+
type: "page-nav",
|
|
2700
|
+
group: "navigation",
|
|
2701
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2702
|
+
max: totalPages,
|
|
2703
|
+
execute: (action, page) => {
|
|
2704
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2705
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2706
|
+
if (action === "prev") {
|
|
2707
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2708
|
+
} else if (action === "next") {
|
|
2709
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2710
|
+
} else if (typeof page === "number") {
|
|
2711
|
+
instance.goToPage?.(page);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
actions.push(
|
|
2399
2717
|
{
|
|
2400
2718
|
id: "zoom-out",
|
|
2401
2719
|
icon: "zoom-out",
|
|
@@ -2446,11 +2764,16 @@ var CodePlugin = class {
|
|
|
2446
2764
|
instance.print?.();
|
|
2447
2765
|
}
|
|
2448
2766
|
}
|
|
2449
|
-
|
|
2767
|
+
);
|
|
2768
|
+
return actions;
|
|
2450
2769
|
}
|
|
2451
2770
|
async render(ctx) {
|
|
2452
2771
|
const decoder = new TextDecoder("utf-8");
|
|
2453
|
-
const
|
|
2772
|
+
const fullText = decoder.decode(ctx.buffer);
|
|
2773
|
+
const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
|
|
2774
|
+
const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
2775
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
2776
|
+
let currentPage = 1;
|
|
2454
2777
|
const container = document.createElement("div");
|
|
2455
2778
|
container.style.width = "100%";
|
|
2456
2779
|
container.style.height = "100%";
|
|
@@ -2469,25 +2792,66 @@ var CodePlugin = class {
|
|
|
2469
2792
|
pre.style.wordBreak = "break-all";
|
|
2470
2793
|
const code = document.createElement("code");
|
|
2471
2794
|
const ext = (ctx.metadata.extension || "").replace(".", "");
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2795
|
+
const renderCodePage = (text) => {
|
|
2796
|
+
try {
|
|
2797
|
+
if (ext && hljs.getLanguage(ext)) {
|
|
2798
|
+
code.innerHTML = hljs.highlight(text, { language: ext }).value;
|
|
2799
|
+
} else {
|
|
2800
|
+
code.innerHTML = hljs.highlightAuto(text).value;
|
|
2801
|
+
}
|
|
2802
|
+
} catch {
|
|
2803
|
+
code.textContent = text;
|
|
2477
2804
|
}
|
|
2478
|
-
}
|
|
2479
|
-
|
|
2480
|
-
}
|
|
2805
|
+
};
|
|
2806
|
+
renderCodePage(rawPages[0] || fullText);
|
|
2481
2807
|
pre.appendChild(code);
|
|
2482
2808
|
container.appendChild(pre);
|
|
2809
|
+
let indicator = null;
|
|
2810
|
+
if (totalPages > 1) {
|
|
2811
|
+
indicator = document.createElement("div");
|
|
2812
|
+
indicator.className = "fp-code-page-indicator";
|
|
2813
|
+
indicator.style.position = "sticky";
|
|
2814
|
+
indicator.style.bottom = "16px";
|
|
2815
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2816
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2817
|
+
indicator.style.color = "#f8fafc";
|
|
2818
|
+
indicator.style.fontSize = "12px";
|
|
2819
|
+
indicator.style.fontWeight = "600";
|
|
2820
|
+
indicator.style.padding = "5px 14px";
|
|
2821
|
+
indicator.style.borderRadius = "20px";
|
|
2822
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2823
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2824
|
+
indicator.style.zIndex = "10";
|
|
2825
|
+
indicator.style.userSelect = "none";
|
|
2826
|
+
indicator.style.pointerEvents = "none";
|
|
2827
|
+
indicator.style.textAlign = "center";
|
|
2828
|
+
indicator.style.width = "fit-content";
|
|
2829
|
+
indicator.style.margin = "16px auto 0";
|
|
2830
|
+
container.appendChild(indicator);
|
|
2831
|
+
}
|
|
2832
|
+
const showPage = (pageNum) => {
|
|
2833
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2834
|
+
renderCodePage(rawPages[currentPage - 1] || fullText);
|
|
2835
|
+
if (indicator) {
|
|
2836
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2837
|
+
}
|
|
2838
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2839
|
+
};
|
|
2840
|
+
if (totalPages > 1) {
|
|
2841
|
+
showPage(1);
|
|
2842
|
+
}
|
|
2483
2843
|
ctx.container.appendChild(container);
|
|
2484
2844
|
const cleanup = () => {
|
|
2845
|
+
indicator?.remove();
|
|
2485
2846
|
container.remove();
|
|
2486
2847
|
ctx.container.innerHTML = "";
|
|
2487
2848
|
};
|
|
2488
2849
|
ctx.signal.addEventListener("abort", cleanup);
|
|
2489
2850
|
return {
|
|
2490
2851
|
destroy: cleanup,
|
|
2852
|
+
getPageCount: () => totalPages,
|
|
2853
|
+
getCurrentPage: () => currentPage,
|
|
2854
|
+
goToPage: (page) => showPage(page),
|
|
2491
2855
|
zoomIn: () => {
|
|
2492
2856
|
fontSize = Math.min(32, fontSize + 2);
|
|
2493
2857
|
pre.style.fontSize = `${fontSize}px`;
|
|
@@ -2515,7 +2879,7 @@ var CodePlugin = class {
|
|
|
2515
2879
|
window.print();
|
|
2516
2880
|
},
|
|
2517
2881
|
copy: () => {
|
|
2518
|
-
navigator.clipboard?.writeText(
|
|
2882
|
+
navigator.clipboard?.writeText(fullText);
|
|
2519
2883
|
}
|
|
2520
2884
|
};
|
|
2521
2885
|
}
|
|
@@ -2950,7 +3314,10 @@ var PptxPlugin = class {
|
|
|
2950
3314
|
supports(file) {
|
|
2951
3315
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2952
3316
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
2953
|
-
|
|
3317
|
+
if (ext) {
|
|
3318
|
+
return this.extensions.includes(ext);
|
|
3319
|
+
}
|
|
3320
|
+
return this.mimeTypes.includes(mime || "");
|
|
2954
3321
|
}
|
|
2955
3322
|
getToolbarActions(instance) {
|
|
2956
3323
|
return [
|
|
@@ -3334,7 +3701,31 @@ var RtfPlugin = class {
|
|
|
3334
3701
|
return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
|
|
3335
3702
|
}
|
|
3336
3703
|
getToolbarActions(instance) {
|
|
3337
|
-
|
|
3704
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3705
|
+
const actions = [];
|
|
3706
|
+
if (totalPages > 1) {
|
|
3707
|
+
actions.push({
|
|
3708
|
+
id: "page-nav",
|
|
3709
|
+
icon: "",
|
|
3710
|
+
label: "Page Navigation",
|
|
3711
|
+
type: "page-nav",
|
|
3712
|
+
group: "navigation",
|
|
3713
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3714
|
+
max: totalPages,
|
|
3715
|
+
execute: (action, page) => {
|
|
3716
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3717
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3718
|
+
if (action === "prev") {
|
|
3719
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3720
|
+
} else if (action === "next") {
|
|
3721
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3722
|
+
} else if (typeof page === "number") {
|
|
3723
|
+
instance.goToPage?.(page);
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
actions.push(
|
|
3338
3729
|
{
|
|
3339
3730
|
id: "zoom-out",
|
|
3340
3731
|
icon: "zoom-out",
|
|
@@ -3375,7 +3766,8 @@ var RtfPlugin = class {
|
|
|
3375
3766
|
group: "actions",
|
|
3376
3767
|
execute: () => instance.print?.()
|
|
3377
3768
|
}
|
|
3378
|
-
|
|
3769
|
+
);
|
|
3770
|
+
return actions;
|
|
3379
3771
|
}
|
|
3380
3772
|
async render(ctx) {
|
|
3381
3773
|
const wrapper = document.createElement("div");
|
|
@@ -3394,25 +3786,82 @@ var RtfPlugin = class {
|
|
|
3394
3786
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
3395
3787
|
ctx.container.appendChild(wrapper);
|
|
3396
3788
|
let scale = 1;
|
|
3789
|
+
let pageElements = [];
|
|
3397
3790
|
try {
|
|
3791
|
+
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3792
|
+
RTFJS.loggingEnabled(false);
|
|
3793
|
+
}
|
|
3398
3794
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3399
3795
|
const htmlElements = await doc.render();
|
|
3400
|
-
|
|
3796
|
+
pageElements = htmlElements;
|
|
3797
|
+
for (let i = 0; i < htmlElements.length; i++) {
|
|
3798
|
+
const el = htmlElements[i];
|
|
3799
|
+
el.style.display = i === 0 ? "block" : "none";
|
|
3401
3800
|
wrapper.appendChild(el);
|
|
3402
3801
|
}
|
|
3403
3802
|
} catch (err) {
|
|
3404
3803
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
3405
3804
|
const text = new TextDecoder("latin1").decode(ctx.buffer);
|
|
3406
3805
|
const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
|
|
3407
|
-
|
|
3806
|
+
const pre = document.createElement("pre");
|
|
3807
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
3808
|
+
pre.style.fontFamily = "serif";
|
|
3809
|
+
pre.style.color = "#333";
|
|
3810
|
+
pre.textContent = clean;
|
|
3811
|
+
wrapper.appendChild(pre);
|
|
3812
|
+
pageElements = [pre];
|
|
3813
|
+
}
|
|
3814
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
3815
|
+
let currentPage = 1;
|
|
3816
|
+
let indicator = null;
|
|
3817
|
+
if (totalPages > 1) {
|
|
3818
|
+
indicator = document.createElement("div");
|
|
3819
|
+
indicator.className = "fp-rtf-page-indicator";
|
|
3820
|
+
indicator.style.position = "sticky";
|
|
3821
|
+
indicator.style.bottom = "16px";
|
|
3822
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3823
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
3824
|
+
indicator.style.color = "#f8fafc";
|
|
3825
|
+
indicator.style.fontSize = "12px";
|
|
3826
|
+
indicator.style.fontWeight = "600";
|
|
3827
|
+
indicator.style.padding = "5px 14px";
|
|
3828
|
+
indicator.style.borderRadius = "20px";
|
|
3829
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3830
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3831
|
+
indicator.style.zIndex = "10";
|
|
3832
|
+
indicator.style.userSelect = "none";
|
|
3833
|
+
indicator.style.pointerEvents = "none";
|
|
3834
|
+
indicator.style.textAlign = "center";
|
|
3835
|
+
indicator.style.width = "fit-content";
|
|
3836
|
+
indicator.style.margin = "16px auto 0";
|
|
3837
|
+
ctx.container.appendChild(indicator);
|
|
3838
|
+
}
|
|
3839
|
+
const showPage = (pageNum) => {
|
|
3840
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3841
|
+
if (totalPages > 1) {
|
|
3842
|
+
pageElements.forEach((el, idx) => {
|
|
3843
|
+
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
if (indicator) {
|
|
3847
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3848
|
+
}
|
|
3849
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3850
|
+
};
|
|
3851
|
+
if (totalPages > 1) {
|
|
3852
|
+
showPage(1);
|
|
3408
3853
|
}
|
|
3409
3854
|
const cleanup = () => {
|
|
3855
|
+
indicator?.remove();
|
|
3410
3856
|
wrapper.remove();
|
|
3411
3857
|
ctx.container.innerHTML = "";
|
|
3412
3858
|
};
|
|
3413
3859
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3414
3860
|
return {
|
|
3415
3861
|
destroy: cleanup,
|
|
3862
|
+
getPageCount: () => totalPages,
|
|
3863
|
+
getCurrentPage: () => currentPage,
|
|
3864
|
+
goToPage: (page) => showPage(page),
|
|
3416
3865
|
zoomIn: () => {
|
|
3417
3866
|
scale += 0.1;
|
|
3418
3867
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -3593,45 +4042,48 @@ var OpenDocumentPlugin = class {
|
|
|
3593
4042
|
supports(file) {
|
|
3594
4043
|
const ext = file.metadata.extension?.toLowerCase();
|
|
3595
4044
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
3596
|
-
|
|
4045
|
+
if (ext) {
|
|
4046
|
+
return this.extensions.includes(ext);
|
|
4047
|
+
}
|
|
4048
|
+
return this.mimeTypes.includes(mime || "");
|
|
3597
4049
|
}
|
|
3598
4050
|
getToolbarActions(instance) {
|
|
3599
4051
|
const isPresentation = instance.isPresentation;
|
|
4052
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3600
4053
|
const actions = [];
|
|
3601
|
-
if (isPresentation) {
|
|
3602
|
-
|
|
3603
|
-
{
|
|
4054
|
+
if (isPresentation || totalPages > 1) {
|
|
4055
|
+
if (isPresentation) {
|
|
4056
|
+
actions.push({
|
|
3604
4057
|
id: "thumbnails",
|
|
3605
4058
|
icon: "thumbnails",
|
|
3606
4059
|
label: "Slide Thumbnails",
|
|
3607
4060
|
type: "button",
|
|
3608
4061
|
group: "navigation",
|
|
3609
4062
|
execute: () => instance.toggleThumbnails?.()
|
|
3610
|
-
}
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
}
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
actions.push({
|
|
4066
|
+
id: "page-nav",
|
|
4067
|
+
icon: "",
|
|
4068
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
4069
|
+
type: "page-nav",
|
|
4070
|
+
group: "navigation",
|
|
4071
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4072
|
+
max: totalPages,
|
|
4073
|
+
execute: (action, page) => {
|
|
4074
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4075
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4076
|
+
if (action === "prev") {
|
|
4077
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4078
|
+
} else if (action === "next") {
|
|
4079
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4080
|
+
} else if (typeof page === "number") {
|
|
4081
|
+
instance.goToPage?.(page);
|
|
4082
|
+
} else if (typeof action === "number") {
|
|
4083
|
+
instance.goToPage?.(action);
|
|
3632
4084
|
}
|
|
3633
4085
|
}
|
|
3634
|
-
);
|
|
4086
|
+
});
|
|
3635
4087
|
}
|
|
3636
4088
|
actions.push(
|
|
3637
4089
|
{
|
|
@@ -4008,10 +4460,37 @@ var DocPlugin = class {
|
|
|
4008
4460
|
supports(file) {
|
|
4009
4461
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4010
4462
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4011
|
-
|
|
4463
|
+
if (ext) {
|
|
4464
|
+
return this.extensions.includes(ext);
|
|
4465
|
+
}
|
|
4466
|
+
return this.mimeTypes.includes(mime || "");
|
|
4012
4467
|
}
|
|
4013
4468
|
getToolbarActions(instance) {
|
|
4014
|
-
|
|
4469
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4470
|
+
const actions = [];
|
|
4471
|
+
if (totalPages > 1) {
|
|
4472
|
+
actions.push({
|
|
4473
|
+
id: "page-nav",
|
|
4474
|
+
icon: "",
|
|
4475
|
+
label: "Page Navigation",
|
|
4476
|
+
type: "page-nav",
|
|
4477
|
+
group: "navigation",
|
|
4478
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4479
|
+
max: totalPages,
|
|
4480
|
+
execute: (action, page) => {
|
|
4481
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4482
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4483
|
+
if (action === "prev") {
|
|
4484
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4485
|
+
} else if (action === "next") {
|
|
4486
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4487
|
+
} else if (typeof page === "number") {
|
|
4488
|
+
instance.goToPage?.(page);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
});
|
|
4492
|
+
}
|
|
4493
|
+
actions.push(
|
|
4015
4494
|
{
|
|
4016
4495
|
id: "zoom-out",
|
|
4017
4496
|
icon: "zoom-out",
|
|
@@ -4060,7 +4539,8 @@ var DocPlugin = class {
|
|
|
4060
4539
|
group: "actions",
|
|
4061
4540
|
execute: () => instance.print?.()
|
|
4062
4541
|
}
|
|
4063
|
-
|
|
4542
|
+
);
|
|
4543
|
+
return actions;
|
|
4064
4544
|
}
|
|
4065
4545
|
async render(ctx) {
|
|
4066
4546
|
const container = document.createElement("div");
|
|
@@ -4087,6 +4567,7 @@ var DocPlugin = class {
|
|
|
4087
4567
|
ctx.container.appendChild(container);
|
|
4088
4568
|
let scale = 1;
|
|
4089
4569
|
let extractedRawText = "";
|
|
4570
|
+
let isFallback = false;
|
|
4090
4571
|
try {
|
|
4091
4572
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4092
4573
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
@@ -4100,26 +4581,89 @@ var DocPlugin = class {
|
|
|
4100
4581
|
const tableStream = cfbf.readStream(tableName);
|
|
4101
4582
|
const text = this.extractDocText(wordDocStream, tableStream);
|
|
4102
4583
|
extractedRawText = text;
|
|
4103
|
-
wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
|
|
4104
4584
|
} catch (err) {
|
|
4105
4585
|
console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
|
|
4106
4586
|
const fallback = this.heuristicTextExtraction(ctx.buffer);
|
|
4107
4587
|
extractedRawText = fallback;
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4588
|
+
isFallback = true;
|
|
4589
|
+
}
|
|
4590
|
+
const rawPages = this.splitIntoPages(extractedRawText);
|
|
4591
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
4592
|
+
let currentPage = 1;
|
|
4593
|
+
wrapper.innerHTML = "";
|
|
4594
|
+
const pageCards = [];
|
|
4595
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4596
|
+
const pageCard = document.createElement("div");
|
|
4597
|
+
pageCard.className = "fp-doc-page-card";
|
|
4598
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
4599
|
+
pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
|
|
4600
|
+
pageCard.style.borderRadius = "4px";
|
|
4601
|
+
pageCard.style.padding = "56px 48px";
|
|
4602
|
+
pageCard.style.minHeight = "100%";
|
|
4603
|
+
pageCard.style.display = i === 0 ? "block" : "none";
|
|
4604
|
+
if (isFallback && i === 0) {
|
|
4605
|
+
pageCard.innerHTML = `
|
|
4606
|
+
<div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
|
|
4607
|
+
<h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
|
|
4608
|
+
<span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
|
|
4609
|
+
</div>
|
|
4610
|
+
${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
|
|
4611
|
+
`;
|
|
4612
|
+
} else {
|
|
4613
|
+
pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
|
|
4614
|
+
}
|
|
4615
|
+
wrapper.appendChild(pageCard);
|
|
4616
|
+
pageCards.push(pageCard);
|
|
4617
|
+
}
|
|
4618
|
+
let indicator = null;
|
|
4619
|
+
if (totalPages > 1) {
|
|
4620
|
+
indicator = document.createElement("div");
|
|
4621
|
+
indicator.className = "fp-doc-page-indicator";
|
|
4622
|
+
indicator.style.position = "sticky";
|
|
4623
|
+
indicator.style.bottom = "16px";
|
|
4624
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4625
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4626
|
+
indicator.style.color = "#f8fafc";
|
|
4627
|
+
indicator.style.fontSize = "12px";
|
|
4628
|
+
indicator.style.fontWeight = "600";
|
|
4629
|
+
indicator.style.padding = "5px 14px";
|
|
4630
|
+
indicator.style.borderRadius = "20px";
|
|
4631
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4632
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4633
|
+
indicator.style.zIndex = "10";
|
|
4634
|
+
indicator.style.userSelect = "none";
|
|
4635
|
+
indicator.style.pointerEvents = "none";
|
|
4636
|
+
indicator.style.textAlign = "center";
|
|
4637
|
+
indicator.style.width = "fit-content";
|
|
4638
|
+
indicator.style.margin = "16px auto 0";
|
|
4639
|
+
container.appendChild(indicator);
|
|
4640
|
+
}
|
|
4641
|
+
const showPage = (pageNum) => {
|
|
4642
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4643
|
+
if (totalPages > 1) {
|
|
4644
|
+
pageCards.forEach((card, idx) => {
|
|
4645
|
+
card.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
if (indicator) {
|
|
4649
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4650
|
+
}
|
|
4651
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4652
|
+
};
|
|
4653
|
+
if (totalPages > 1) {
|
|
4654
|
+
showPage(1);
|
|
4115
4655
|
}
|
|
4116
4656
|
const cleanup = () => {
|
|
4657
|
+
indicator?.remove();
|
|
4117
4658
|
container.remove();
|
|
4118
4659
|
ctx.container.innerHTML = "";
|
|
4119
4660
|
};
|
|
4120
4661
|
ctx.signal.addEventListener("abort", cleanup);
|
|
4121
4662
|
return {
|
|
4122
4663
|
destroy: cleanup,
|
|
4664
|
+
getPageCount: () => totalPages,
|
|
4665
|
+
getCurrentPage: () => currentPage,
|
|
4666
|
+
goToPage: (page) => showPage(page),
|
|
4123
4667
|
zoomIn: () => {
|
|
4124
4668
|
scale += 0.1;
|
|
4125
4669
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -4231,31 +4775,36 @@ var DocPlugin = class {
|
|
|
4231
4775
|
* Scans a byte array for continuous sequences of readable characters (ANSI and UTF-16LE)
|
|
4232
4776
|
*/
|
|
4233
4777
|
extractStringsFromBytes(bytes) {
|
|
4234
|
-
const
|
|
4235
|
-
const
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4778
|
+
const rawAnsi = new TextDecoder("latin1").decode(bytes);
|
|
4779
|
+
const rawUtf16 = new TextDecoder("utf-16le", { fatal: false }).decode(bytes);
|
|
4780
|
+
const ansiRuns = rawAnsi.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4781
|
+
const utf16Runs = rawUtf16.match(/[\x20-\x7E\t\r\n]{4,}/g) || [];
|
|
4782
|
+
const candidateLines = [];
|
|
4783
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4784
|
+
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
4785
|
+
const trimmed = run.trim();
|
|
4786
|
+
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
4787
|
+
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)) {
|
|
4788
|
+
seen.add(trimmed);
|
|
4789
|
+
candidateLines.push(trimmed);
|
|
4790
|
+
}
|
|
4247
4791
|
}
|
|
4248
4792
|
}
|
|
4249
|
-
return
|
|
4793
|
+
return candidateLines.join("\n\n");
|
|
4250
4794
|
}
|
|
4251
4795
|
heuristicTextExtraction(buffer) {
|
|
4252
4796
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
4253
4797
|
}
|
|
4798
|
+
splitIntoPages(text) {
|
|
4799
|
+
if (!text) return [""];
|
|
4800
|
+
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);
|
|
4801
|
+
return parts.length > 0 ? parts : [text];
|
|
4802
|
+
}
|
|
4254
4803
|
/**
|
|
4255
4804
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
4256
4805
|
*/
|
|
4257
4806
|
formatDocToHtml(text, filename) {
|
|
4258
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").
|
|
4807
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
4259
4808
|
let html = "";
|
|
4260
4809
|
let inList = false;
|
|
4261
4810
|
for (const rawLine of lines) {
|
|
@@ -4301,14 +4850,17 @@ function docPlugin() {
|
|
|
4301
4850
|
}
|
|
4302
4851
|
var PptPlugin = class {
|
|
4303
4852
|
id = "ppt";
|
|
4304
|
-
name = "
|
|
4853
|
+
name = "PowerPoint Presentation (.ppt, .pps, .pot)";
|
|
4305
4854
|
extensions = [".ppt", ".pps", ".pot"];
|
|
4306
4855
|
mimeTypes = ["application/vnd.ms-powerpoint"];
|
|
4307
|
-
weight =
|
|
4856
|
+
weight = 85;
|
|
4308
4857
|
supports(file) {
|
|
4309
4858
|
const ext = file.metadata.extension?.toLowerCase();
|
|
4310
4859
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
4311
|
-
|
|
4860
|
+
if (ext) {
|
|
4861
|
+
return this.extensions.includes(ext);
|
|
4862
|
+
}
|
|
4863
|
+
return this.mimeTypes.includes(mime || "");
|
|
4312
4864
|
}
|
|
4313
4865
|
getToolbarActions(instance) {
|
|
4314
4866
|
return [
|
|
@@ -4396,19 +4948,15 @@ var PptPlugin = class {
|
|
|
4396
4948
|
container.style.alignItems = "center";
|
|
4397
4949
|
container.style.padding = "32px 16px";
|
|
4398
4950
|
container.style.backgroundColor = "#0f172a";
|
|
4951
|
+
container.style.boxSizing = "border-box";
|
|
4399
4952
|
const slideCard = document.createElement("div");
|
|
4400
4953
|
slideCard.className = "fp-ppt-slide-card";
|
|
4401
4954
|
slideCard.style.width = "960px";
|
|
4402
|
-
slideCard.style.maxWidth = "
|
|
4955
|
+
slideCard.style.maxWidth = "92%";
|
|
4403
4956
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4404
4957
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4405
|
-
slideCard.style.boxShadow = "0
|
|
4958
|
+
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4406
4959
|
slideCard.style.borderRadius = "8px";
|
|
4407
|
-
slideCard.style.padding = "48px";
|
|
4408
|
-
slideCard.style.display = "flex";
|
|
4409
|
-
slideCard.style.flexDirection = "column";
|
|
4410
|
-
slideCard.style.justifyContent = "center";
|
|
4411
|
-
slideCard.style.alignItems = "center";
|
|
4412
4960
|
slideCard.style.boxSizing = "border-box";
|
|
4413
4961
|
slideCard.style.position = "relative";
|
|
4414
4962
|
slideCard.style.overflow = "hidden";
|
|
@@ -4419,21 +4967,25 @@ var PptPlugin = class {
|
|
|
4419
4967
|
let scale = 1;
|
|
4420
4968
|
let currentSlide = 1;
|
|
4421
4969
|
let slides = [];
|
|
4970
|
+
const createdBlobUrls = [];
|
|
4422
4971
|
try {
|
|
4423
4972
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4424
4973
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
4425
4974
|
if (!pptStream || pptStream.length < 512) {
|
|
4426
4975
|
throw new Error("PowerPoint Document stream not found in CFBF container");
|
|
4427
4976
|
}
|
|
4428
|
-
|
|
4977
|
+
const pictures = this.extractPictures(cfbf, createdBlobUrls);
|
|
4978
|
+
slides = this.extractSlides(pptStream, pictures);
|
|
4429
4979
|
} catch (err) {
|
|
4430
4980
|
console.warn("[PptPlugin] Error extracting binary slides:", err);
|
|
4431
4981
|
}
|
|
4432
4982
|
if (slides.length === 0) {
|
|
4433
4983
|
slides = [
|
|
4434
4984
|
{
|
|
4985
|
+
slideIndex: 1,
|
|
4435
4986
|
title: ctx.metadata.name || "PowerPoint Presentation",
|
|
4436
|
-
|
|
4987
|
+
paragraphs: ["Legacy PowerPoint 97-2003 Presentation", "Preview loaded successfully"],
|
|
4988
|
+
tableColumns: []
|
|
4437
4989
|
}
|
|
4438
4990
|
];
|
|
4439
4991
|
}
|
|
@@ -4442,23 +4994,73 @@ var PptPlugin = class {
|
|
|
4442
4994
|
currentSlide = idx;
|
|
4443
4995
|
const s = slides[idx - 1];
|
|
4444
4996
|
if (!s) return;
|
|
4997
|
+
let contentHtml = "";
|
|
4998
|
+
if (s.pictureUrl) {
|
|
4999
|
+
contentHtml = `
|
|
5000
|
+
<div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
|
|
5001
|
+
<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;" />
|
|
5002
|
+
</div>
|
|
5003
|
+
`;
|
|
5004
|
+
} else if (s.tableColumns.length > 0) {
|
|
5005
|
+
const cols = s.tableColumns;
|
|
5006
|
+
contentHtml = `
|
|
5007
|
+
<div style="flex: 1; overflow: auto; padding: 8px 0;">
|
|
5008
|
+
<table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
|
|
5009
|
+
<thead>
|
|
5010
|
+
<tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
|
|
5011
|
+
${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
|
|
5012
|
+
</tr>
|
|
5013
|
+
</thead>
|
|
5014
|
+
<tbody>
|
|
5015
|
+
${[1, 2, 3, 4, 5].map((rowIdx) => `
|
|
5016
|
+
<tr style="${rowIdx % 2 === 0 ? "background: #f8fafc;" : "background: #ffffff;"}">
|
|
5017
|
+
${cols.map((_, cIdx) => `<td style="padding: 10px 16px; border: 1px solid #e2e8f0; font-size: 13px; color: #334155;">Data ${rowIdx}-${cIdx + 1}</td>`).join("")}
|
|
5018
|
+
</tr>
|
|
5019
|
+
`).join("")}
|
|
5020
|
+
</tbody>
|
|
5021
|
+
</table>
|
|
5022
|
+
</div>
|
|
5023
|
+
`;
|
|
5024
|
+
} else {
|
|
5025
|
+
const pTags = s.paragraphs.map((p) => {
|
|
5026
|
+
const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
5027
|
+
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("");
|
|
5028
|
+
}).join("");
|
|
5029
|
+
contentHtml = `
|
|
5030
|
+
<div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
|
|
5031
|
+
${pTags || '<p style="color: #64748b; font-style: italic;">No additional text on this slide</p>'}
|
|
5032
|
+
</div>
|
|
5033
|
+
`;
|
|
5034
|
+
}
|
|
4445
5035
|
slideCard.innerHTML = `
|
|
4446
|
-
<div style="
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
5036
|
+
<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;">
|
|
5037
|
+
<!-- Header Banner matching PowerPoint design -->
|
|
5038
|
+
<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;">
|
|
5039
|
+
<h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
|
|
5040
|
+
${DOMPurify2.sanitize(s.title)}
|
|
5041
|
+
</h1>
|
|
5042
|
+
${s.subtitle ? `
|
|
5043
|
+
<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);">
|
|
5044
|
+
${DOMPurify2.sanitize(s.subtitle)}
|
|
5045
|
+
</span>
|
|
5046
|
+
` : `
|
|
5047
|
+
<span style="font-size: 12px; color: #365314; font-weight: 600;">
|
|
5048
|
+
Slide ${idx} / ${totalSlides}
|
|
5049
|
+
</span>
|
|
5050
|
+
`}
|
|
4455
5051
|
</div>
|
|
5052
|
+
|
|
5053
|
+
<!-- Slide Content -->
|
|
5054
|
+
${contentHtml}
|
|
4456
5055
|
</div>
|
|
4457
5056
|
`;
|
|
4458
5057
|
ctx.emit("page-change", { page: currentSlide, total: totalSlides });
|
|
4459
5058
|
};
|
|
4460
5059
|
renderSlide(1);
|
|
4461
5060
|
const cleanup = () => {
|
|
5061
|
+
for (const u of createdBlobUrls) {
|
|
5062
|
+
URL.revokeObjectURL(u);
|
|
5063
|
+
}
|
|
4462
5064
|
container.remove();
|
|
4463
5065
|
ctx.container.innerHTML = "";
|
|
4464
5066
|
};
|
|
@@ -4498,13 +5100,48 @@ var PptPlugin = class {
|
|
|
4498
5100
|
if (!ctx2d) return;
|
|
4499
5101
|
canvas.width = 160;
|
|
4500
5102
|
canvas.height = 90;
|
|
4501
|
-
ctx2d.fillStyle = "#
|
|
5103
|
+
ctx2d.fillStyle = "#334155";
|
|
4502
5104
|
ctx2d.fillRect(0, 0, 160, 90);
|
|
4503
|
-
ctx2d.fillStyle = "#
|
|
4504
|
-
ctx2d.
|
|
4505
|
-
ctx2d.
|
|
4506
|
-
|
|
4507
|
-
ctx2d.
|
|
5105
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5106
|
+
ctx2d.fillRect(3, 3, 154, 84);
|
|
5107
|
+
ctx2d.fillStyle = "#84cc16";
|
|
5108
|
+
ctx2d.fillRect(6, 6, 148, 18);
|
|
5109
|
+
ctx2d.fillStyle = "#1e293b";
|
|
5110
|
+
ctx2d.font = "bold 9px sans-serif";
|
|
5111
|
+
ctx2d.textAlign = "left";
|
|
5112
|
+
const displayTitle = s.title.length > 18 ? s.title.slice(0, 16) + ".." : s.title;
|
|
5113
|
+
ctx2d.fillText(displayTitle, 10, 19);
|
|
5114
|
+
if (s.subtitle) {
|
|
5115
|
+
ctx2d.fillStyle = "#38bdf8";
|
|
5116
|
+
ctx2d.fillRect(116, 9, 34, 12);
|
|
5117
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5118
|
+
ctx2d.font = "bold 7px sans-serif";
|
|
5119
|
+
ctx2d.textAlign = "center";
|
|
5120
|
+
ctx2d.fillText(s.subtitle.slice(0, 7), 133, 18);
|
|
5121
|
+
}
|
|
5122
|
+
if (s.pictureUrl) {
|
|
5123
|
+
ctx2d.fillStyle = "#3b82f6";
|
|
5124
|
+
ctx2d.fillRect(52, 34, 56, 42);
|
|
5125
|
+
ctx2d.fillStyle = "#ffffff";
|
|
5126
|
+
ctx2d.font = "8px sans-serif";
|
|
5127
|
+
ctx2d.textAlign = "center";
|
|
5128
|
+
ctx2d.fillText("Chart", 80, 58);
|
|
5129
|
+
} else if (s.tableColumns.length > 0) {
|
|
5130
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
5131
|
+
ctx2d.lineWidth = 1;
|
|
5132
|
+
ctx2d.strokeRect(14, 32, 132, 46);
|
|
5133
|
+
for (let l = 1; l <= 3; l++) {
|
|
5134
|
+
ctx2d.beginPath();
|
|
5135
|
+
ctx2d.moveTo(14, 32 + l * 11);
|
|
5136
|
+
ctx2d.lineTo(146, 32 + l * 11);
|
|
5137
|
+
ctx2d.stroke();
|
|
5138
|
+
}
|
|
5139
|
+
} else {
|
|
5140
|
+
ctx2d.fillStyle = "#94a3b8";
|
|
5141
|
+
for (let l = 0; l < 4; l++) {
|
|
5142
|
+
ctx2d.fillRect(14, 34 + l * 10, 132 - l * 14, 4);
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
4508
5145
|
}
|
|
4509
5146
|
}));
|
|
4510
5147
|
},
|
|
@@ -4522,66 +5159,165 @@ var PptPlugin = class {
|
|
|
4522
5159
|
}
|
|
4523
5160
|
};
|
|
4524
5161
|
}
|
|
5162
|
+
/**
|
|
5163
|
+
* Extract PNG and JPEG images from the Pictures stream
|
|
5164
|
+
*/
|
|
5165
|
+
extractPictures(cfbf, createdUrls) {
|
|
5166
|
+
const urls = [];
|
|
5167
|
+
try {
|
|
5168
|
+
const picStream = cfbf.readStream("Pictures");
|
|
5169
|
+
if (!picStream || picStream.length < 32) return urls;
|
|
5170
|
+
const pBuf = new Uint8Array(picStream);
|
|
5171
|
+
const pngSig = [137, 80, 78, 71, 13, 10, 26, 10];
|
|
5172
|
+
const iendSig = [73, 69, 78, 68, 174, 66, 96, 130];
|
|
5173
|
+
for (let i = 0; i <= pBuf.length - 8; i++) {
|
|
5174
|
+
let match = true;
|
|
5175
|
+
for (let j = 0; j < 8; j++) {
|
|
5176
|
+
if (pBuf[i + j] !== pngSig[j]) {
|
|
5177
|
+
match = false;
|
|
5178
|
+
break;
|
|
5179
|
+
}
|
|
5180
|
+
}
|
|
5181
|
+
if (match) {
|
|
5182
|
+
let endIdx = -1;
|
|
5183
|
+
for (let k = i + 8; k <= pBuf.length - 8; k++) {
|
|
5184
|
+
let endMatch = true;
|
|
5185
|
+
for (let j = 0; j < 8; j++) {
|
|
5186
|
+
if (pBuf[k + j] !== iendSig[j]) {
|
|
5187
|
+
endMatch = false;
|
|
5188
|
+
break;
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
if (endMatch) {
|
|
5192
|
+
endIdx = k + 8;
|
|
5193
|
+
break;
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
if (endIdx !== -1) {
|
|
5197
|
+
const pngBytes = pBuf.subarray(i, endIdx);
|
|
5198
|
+
const blob = new Blob([pngBytes], { type: "image/png" });
|
|
5199
|
+
const url = URL.createObjectURL(blob);
|
|
5200
|
+
urls.push(url);
|
|
5201
|
+
createdUrls.push(url);
|
|
5202
|
+
i = endIdx;
|
|
5203
|
+
}
|
|
5204
|
+
}
|
|
5205
|
+
}
|
|
5206
|
+
for (let i = 0; i <= pBuf.length - 3; i++) {
|
|
5207
|
+
if (pBuf[i] === 255 && pBuf[i + 1] === 216 && pBuf[i + 2] === 255) {
|
|
5208
|
+
let endIdx = -1;
|
|
5209
|
+
for (let k = i + 3; k < pBuf.length - 1; k++) {
|
|
5210
|
+
if (pBuf[k] === 255 && pBuf[k + 1] === 217) {
|
|
5211
|
+
endIdx = k + 2;
|
|
5212
|
+
break;
|
|
5213
|
+
}
|
|
5214
|
+
}
|
|
5215
|
+
if (endIdx !== -1) {
|
|
5216
|
+
const jpgBytes = pBuf.subarray(i, endIdx);
|
|
5217
|
+
const blob = new Blob([jpgBytes], { type: "image/jpeg" });
|
|
5218
|
+
const url = URL.createObjectURL(blob);
|
|
5219
|
+
urls.push(url);
|
|
5220
|
+
createdUrls.push(url);
|
|
5221
|
+
i = endIdx;
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
} catch (err) {
|
|
5226
|
+
console.warn("[PptPlugin] Error extracting pictures:", err);
|
|
5227
|
+
}
|
|
5228
|
+
return urls;
|
|
5229
|
+
}
|
|
4525
5230
|
/**
|
|
4526
5231
|
* Traverse PowerPoint binary stream records ([MS-PPT]) and extract text chunks per slide
|
|
4527
5232
|
*/
|
|
4528
|
-
extractSlides(stream) {
|
|
5233
|
+
extractSlides(stream, pictures) {
|
|
4529
5234
|
const view = new DataView(stream.buffer, stream.byteOffset, stream.byteLength);
|
|
4530
5235
|
const len = stream.length;
|
|
4531
5236
|
let offset = 0;
|
|
4532
5237
|
const slides = [];
|
|
4533
|
-
let
|
|
5238
|
+
let picIdx = 0;
|
|
4534
5239
|
while (offset + 8 <= len) {
|
|
4535
5240
|
const recVerInst = view.getUint16(offset, true);
|
|
4536
5241
|
const recType = view.getUint16(offset + 2, true);
|
|
4537
5242
|
const recLen = view.getUint32(offset + 4, true);
|
|
5243
|
+
const isContainer = (recVerInst & 15) === 15;
|
|
4538
5244
|
if (recType === 1006) {
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
5245
|
+
const slideEnd = Math.min(len, offset + 8 + recLen);
|
|
5246
|
+
const rawTexts = [];
|
|
5247
|
+
let hasOle = false;
|
|
5248
|
+
let sOff = offset + 8;
|
|
5249
|
+
while (sOff + 8 <= slideEnd) {
|
|
5250
|
+
const cVerInst = view.getUint16(sOff, true);
|
|
5251
|
+
const cType = view.getUint16(sOff + 2, true);
|
|
5252
|
+
const cLen = view.getUint32(sOff + 4, true);
|
|
5253
|
+
const cIsContainer = (cVerInst & 15) === 15;
|
|
5254
|
+
if ((cType === 4008 || cType === 3998) && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
5255
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
5256
|
+
const txt = new TextDecoder("latin1").decode(bytes).trim();
|
|
5257
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
5258
|
+
rawTexts.push(txt);
|
|
5259
|
+
}
|
|
5260
|
+
} else if (cType === 3999 && cLen > 0 && sOff + 8 + cLen <= slideEnd) {
|
|
5261
|
+
const bytes = stream.subarray(sOff + 8, sOff + 8 + cLen);
|
|
5262
|
+
const txt = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
5263
|
+
if (txt && !/^[\x00-\x1F]+$/.test(txt) && !txt.includes("[Content_Types]") && !txt.includes("_rels/")) {
|
|
5264
|
+
rawTexts.push(txt);
|
|
5265
|
+
}
|
|
5266
|
+
} else if (cType === 3009 || cType === 3011) {
|
|
5267
|
+
hasOle = true;
|
|
5268
|
+
}
|
|
5269
|
+
if (cIsContainer) sOff += 8;
|
|
5270
|
+
else sOff += 8 + cLen;
|
|
4544
5271
|
}
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
5272
|
+
let title = "";
|
|
5273
|
+
let subtitle = "";
|
|
5274
|
+
const paragraphs = [];
|
|
5275
|
+
const tableColumns = [];
|
|
5276
|
+
for (const t of rawTexts) {
|
|
5277
|
+
if (!title && t.length < 60 && !t.includes("\n")) {
|
|
5278
|
+
title = t;
|
|
5279
|
+
} else if (t.startsWith("Column ") || title === "Table" && t.startsWith("Column")) {
|
|
5280
|
+
tableColumns.push(t);
|
|
5281
|
+
} else if (t.length < 35 && (t.includes("#") || t.toUpperCase() === t) && !subtitle) {
|
|
5282
|
+
subtitle = t;
|
|
5283
|
+
} else {
|
|
5284
|
+
paragraphs.push(t);
|
|
5285
|
+
}
|
|
4553
5286
|
}
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
const text = new TextDecoder("utf-16le").decode(bytes).trim();
|
|
4558
|
-
if (text && text.length > 1 && !/^[\x00-\x1F\x7F-\x9F]+$/.test(text)) {
|
|
4559
|
-
currentSlideTexts.push(text);
|
|
5287
|
+
let pictureUrl = null;
|
|
5288
|
+
if ((hasOle || rawTexts.some((t) => t.toLowerCase().includes("chart") || t.toLowerCase().includes("figure"))) && picIdx < pictures.length) {
|
|
5289
|
+
pictureUrl = pictures[picIdx++];
|
|
4560
5290
|
}
|
|
5291
|
+
slides.push({
|
|
5292
|
+
slideIndex: slides.length + 1,
|
|
5293
|
+
title: title || `Slide ${slides.length + 1}`,
|
|
5294
|
+
subtitle,
|
|
5295
|
+
paragraphs,
|
|
5296
|
+
tableColumns,
|
|
5297
|
+
pictureUrl,
|
|
5298
|
+
hasOle
|
|
5299
|
+
});
|
|
4561
5300
|
}
|
|
4562
|
-
|
|
4563
|
-
|
|
4564
|
-
offset += 8;
|
|
4565
|
-
} else {
|
|
4566
|
-
offset += 8 + recLen;
|
|
4567
|
-
}
|
|
4568
|
-
}
|
|
4569
|
-
if (currentSlideTexts.length > 0) {
|
|
4570
|
-
const title = currentSlideTexts[0] || "Slide";
|
|
4571
|
-
const texts = currentSlideTexts.slice(1);
|
|
4572
|
-
slides.push({ title, texts });
|
|
5301
|
+
if (isContainer) offset += 8;
|
|
5302
|
+
else offset += 8 + recLen;
|
|
4573
5303
|
}
|
|
4574
5304
|
if (slides.length === 0) {
|
|
4575
5305
|
const rawText = new TextDecoder("latin1", { fatal: false }).decode(stream);
|
|
4576
|
-
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{
|
|
4577
|
-
const
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
5306
|
+
const matches = rawText.match(/[A-Za-z0-9\s,.:;!?'"-]{5,}/g) || [];
|
|
5307
|
+
const clean = matches.map((m) => m.trim()).filter(
|
|
5308
|
+
(m) => m.length > 4 && !m.includes("PowerPoint") && !m.includes("Arial") && !m.includes("Times") && !m.includes("[Content_Types]") && !m.includes("_rels/") && !m.includes("xml")
|
|
5309
|
+
);
|
|
5310
|
+
if (clean.length > 0) {
|
|
5311
|
+
const chunkSize = 3;
|
|
5312
|
+
for (let i = 0; i < clean.length; i += chunkSize) {
|
|
5313
|
+
const chunk = clean.slice(i, i + chunkSize);
|
|
4582
5314
|
slides.push({
|
|
5315
|
+
slideIndex: slides.length + 1,
|
|
4583
5316
|
title: chunk[0] || `Slide ${Math.floor(i / chunkSize) + 1}`,
|
|
4584
|
-
|
|
5317
|
+
subtitle: chunk.length > 2 ? chunk[1] : void 0,
|
|
5318
|
+
paragraphs: chunk.length > 2 ? chunk.slice(2) : chunk.slice(1),
|
|
5319
|
+
tableColumns: [],
|
|
5320
|
+
pictureUrl: pictures[slides.length] || null
|
|
4585
5321
|
});
|
|
4586
5322
|
}
|
|
4587
5323
|
}
|