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