@files-preview-app/preview-file 1.2.4 → 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 +645 -158
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +644 -158
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +645 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +644 -158
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +645 -158
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +644 -158
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +645 -158
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +644 -158
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import DOMPurify2 from 'dompurify';
|
|
2
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
2
3
|
import * as docx from 'docx-preview';
|
|
3
4
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
4
5
|
import * as XLSX from 'xlsx';
|
|
@@ -522,11 +523,21 @@ var ToolbarController = class {
|
|
|
522
523
|
el;
|
|
523
524
|
toolbarEl;
|
|
524
525
|
actions = [];
|
|
526
|
+
pageInputEl = null;
|
|
527
|
+
pageLabelEl = null;
|
|
525
528
|
constructor(container) {
|
|
526
529
|
this.el = container;
|
|
527
530
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
528
531
|
this.el.appendChild(this.toolbarEl);
|
|
529
532
|
}
|
|
533
|
+
setPage(page, max) {
|
|
534
|
+
if (this.pageInputEl) {
|
|
535
|
+
this.pageInputEl.value = page.toString();
|
|
536
|
+
}
|
|
537
|
+
if (max !== void 0 && this.pageLabelEl) {
|
|
538
|
+
this.pageLabelEl.textContent = ` / ${max}`;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
530
541
|
update(actions) {
|
|
531
542
|
this.actions = actions;
|
|
532
543
|
this.render();
|
|
@@ -569,6 +580,7 @@ var ToolbarController = class {
|
|
|
569
580
|
if (action.type === "separator") {
|
|
570
581
|
groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
|
|
571
582
|
} else if (action.type === "page-nav") {
|
|
583
|
+
const max = action.max ?? 1;
|
|
572
584
|
const prevBtn = this.createButton(
|
|
573
585
|
"prev",
|
|
574
586
|
ICON_PAGE_PREV,
|
|
@@ -587,7 +599,6 @@ var ToolbarController = class {
|
|
|
587
599
|
"Next Page",
|
|
588
600
|
() => {
|
|
589
601
|
const cur = parseInt(input.value, 10) || 1;
|
|
590
|
-
const max = action.max ?? 1;
|
|
591
602
|
if (cur < max) {
|
|
592
603
|
input.value = (cur + 1).toString();
|
|
593
604
|
action.execute("next", cur + 1);
|
|
@@ -599,15 +610,18 @@ var ToolbarController = class {
|
|
|
599
610
|
type: "number",
|
|
600
611
|
value: (action.value ?? 1).toString(),
|
|
601
612
|
min: "1",
|
|
602
|
-
max:
|
|
613
|
+
max: max.toString()
|
|
603
614
|
});
|
|
604
615
|
input.addEventListener("change", () => {
|
|
605
|
-
|
|
606
|
-
if (
|
|
607
|
-
|
|
608
|
-
|
|
616
|
+
let val = parseInt(input.value, 10);
|
|
617
|
+
if (isNaN(val)) val = 1;
|
|
618
|
+
val = Math.max(1, Math.min(max, val));
|
|
619
|
+
input.value = val.toString();
|
|
620
|
+
action.execute("go", val);
|
|
609
621
|
});
|
|
610
|
-
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${
|
|
622
|
+
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
623
|
+
this.pageInputEl = input;
|
|
624
|
+
this.pageLabelEl = label;
|
|
611
625
|
groupEl.appendChild(prevBtn);
|
|
612
626
|
groupEl.appendChild(input);
|
|
613
627
|
groupEl.appendChild(label);
|
|
@@ -785,7 +799,13 @@ var FilePreviewViewer = class {
|
|
|
785
799
|
buffer,
|
|
786
800
|
options,
|
|
787
801
|
signal,
|
|
788
|
-
emit: (event, payload) =>
|
|
802
|
+
emit: (event, payload) => {
|
|
803
|
+
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
804
|
+
const total = payload.total ?? payload.totalPages;
|
|
805
|
+
this.toolbar?.setPage(payload.page, total);
|
|
806
|
+
}
|
|
807
|
+
this.eventEmitter.emit(event, payload);
|
|
808
|
+
}
|
|
789
809
|
});
|
|
790
810
|
this.activeInstance = instance;
|
|
791
811
|
this.hideLoading();
|
|
@@ -819,6 +839,7 @@ var FilePreviewViewer = class {
|
|
|
819
839
|
if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
|
|
820
840
|
this.thumbnailPanel.update(thumbnails, (index) => {
|
|
821
841
|
instance.goToPage?.(index + 1);
|
|
842
|
+
this.toolbar?.setPage(index + 1);
|
|
822
843
|
});
|
|
823
844
|
if (options.showThumbnails) {
|
|
824
845
|
this.thumbnailPanel.show();
|
|
@@ -944,9 +965,13 @@ var FilePreviewViewer = class {
|
|
|
944
965
|
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
945
966
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
946
967
|
this.activeInstance.goToPage?.(cur + 1);
|
|
968
|
+
const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
|
|
969
|
+
this.toolbar?.setPage(nextCur);
|
|
947
970
|
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
948
971
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
949
972
|
this.activeInstance.goToPage?.(Math.max(1, cur - 1));
|
|
973
|
+
const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
|
|
974
|
+
this.toolbar?.setPage(prevCur);
|
|
950
975
|
} else if (e.key === "+" || e.key === "=") {
|
|
951
976
|
this.activeInstance.zoomIn?.();
|
|
952
977
|
} else if (e.key === "-" || e.key === "_") {
|
|
@@ -1212,30 +1237,62 @@ var CfbfReader = class {
|
|
|
1212
1237
|
return result.subarray(0, targetSize);
|
|
1213
1238
|
}
|
|
1214
1239
|
};
|
|
1215
|
-
|
|
1216
|
-
|
|
1240
|
+
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1241
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1242
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1217
1245
|
var PdfPlugin = class {
|
|
1218
1246
|
id = "pdf";
|
|
1219
|
-
name = "PDF Preview";
|
|
1247
|
+
name = "PDF Document Preview";
|
|
1220
1248
|
extensions = [".pdf"];
|
|
1221
1249
|
mimeTypes = ["application/pdf"];
|
|
1222
1250
|
weight = 100;
|
|
1223
1251
|
supports(file) {
|
|
1224
1252
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1225
1253
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1226
|
-
|
|
1254
|
+
if (ext) return this.extensions.includes(ext);
|
|
1255
|
+
return this.mimeTypes.includes(mime || "");
|
|
1227
1256
|
}
|
|
1228
1257
|
getToolbarActions(instance) {
|
|
1258
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1259
|
+
const curPage = instance.getCurrentPage?.() ?? 1;
|
|
1229
1260
|
return [
|
|
1261
|
+
{
|
|
1262
|
+
id: "thumbnails",
|
|
1263
|
+
icon: "thumbnails",
|
|
1264
|
+
label: "Page Thumbnails",
|
|
1265
|
+
type: "button",
|
|
1266
|
+
group: "navigation",
|
|
1267
|
+
execute: () => instance.toggleThumbnails?.()
|
|
1268
|
+
},
|
|
1269
|
+
{
|
|
1270
|
+
id: "page-nav",
|
|
1271
|
+
icon: "",
|
|
1272
|
+
label: "Page Navigation",
|
|
1273
|
+
type: "page-nav",
|
|
1274
|
+
group: "navigation",
|
|
1275
|
+
value: curPage,
|
|
1276
|
+
max: totalPages,
|
|
1277
|
+
execute: (action, page) => {
|
|
1278
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1279
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1280
|
+
if (action === "prev") {
|
|
1281
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1282
|
+
} else if (action === "next") {
|
|
1283
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1284
|
+
} else if (typeof page === "number") {
|
|
1285
|
+
instance.goToPage?.(page);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
},
|
|
1230
1289
|
{
|
|
1231
1290
|
id: "zoom-out",
|
|
1232
1291
|
icon: "zoom-out",
|
|
1233
1292
|
label: "Zoom Out",
|
|
1234
1293
|
type: "button",
|
|
1235
1294
|
group: "zoom",
|
|
1236
|
-
execute: () =>
|
|
1237
|
-
instance.zoomOut?.();
|
|
1238
|
-
}
|
|
1295
|
+
execute: () => instance.zoomOut?.()
|
|
1239
1296
|
},
|
|
1240
1297
|
{
|
|
1241
1298
|
id: "zoom-in",
|
|
@@ -1243,9 +1300,7 @@ var PdfPlugin = class {
|
|
|
1243
1300
|
label: "Zoom In",
|
|
1244
1301
|
type: "button",
|
|
1245
1302
|
group: "zoom",
|
|
1246
|
-
execute: () =>
|
|
1247
|
-
instance.zoomIn?.();
|
|
1248
|
-
}
|
|
1303
|
+
execute: () => instance.zoomIn?.()
|
|
1249
1304
|
},
|
|
1250
1305
|
{
|
|
1251
1306
|
id: "fit-page",
|
|
@@ -1253,39 +1308,23 @@ var PdfPlugin = class {
|
|
|
1253
1308
|
label: "Fit to Page",
|
|
1254
1309
|
type: "button",
|
|
1255
1310
|
group: "zoom",
|
|
1256
|
-
execute: () =>
|
|
1257
|
-
instance.fitToPage?.();
|
|
1258
|
-
}
|
|
1311
|
+
execute: () => instance.fitToPage?.()
|
|
1259
1312
|
},
|
|
1260
1313
|
{
|
|
1261
1314
|
id: "rotate-cw",
|
|
1262
1315
|
icon: "rotate-cw",
|
|
1263
|
-
label: "Rotate",
|
|
1316
|
+
label: "Rotate Clockwise",
|
|
1264
1317
|
type: "button",
|
|
1265
1318
|
group: "view",
|
|
1266
|
-
execute: () =>
|
|
1267
|
-
instance.rotateCW?.();
|
|
1268
|
-
}
|
|
1269
|
-
},
|
|
1270
|
-
{
|
|
1271
|
-
id: "page-nav",
|
|
1272
|
-
icon: "page-nav",
|
|
1273
|
-
label: "Page Navigation",
|
|
1274
|
-
type: "page-nav",
|
|
1275
|
-
group: "navigation",
|
|
1276
|
-
execute: (page) => {
|
|
1277
|
-
if (typeof page === "number") instance.goToPage?.(page);
|
|
1278
|
-
}
|
|
1319
|
+
execute: () => instance.rotateCW?.()
|
|
1279
1320
|
},
|
|
1280
1321
|
{
|
|
1281
1322
|
id: "download",
|
|
1282
1323
|
icon: "download",
|
|
1283
|
-
label: "Download",
|
|
1324
|
+
label: "Download PDF",
|
|
1284
1325
|
type: "button",
|
|
1285
1326
|
group: "actions",
|
|
1286
|
-
execute: () =>
|
|
1287
|
-
instance.download?.();
|
|
1288
|
-
}
|
|
1327
|
+
execute: () => instance.download?.()
|
|
1289
1328
|
},
|
|
1290
1329
|
{
|
|
1291
1330
|
id: "print",
|
|
@@ -1293,99 +1332,213 @@ var PdfPlugin = class {
|
|
|
1293
1332
|
label: "Print",
|
|
1294
1333
|
type: "button",
|
|
1295
1334
|
group: "actions",
|
|
1296
|
-
execute: () =>
|
|
1297
|
-
instance.print?.();
|
|
1298
|
-
}
|
|
1335
|
+
execute: () => instance.print?.()
|
|
1299
1336
|
}
|
|
1300
1337
|
];
|
|
1301
1338
|
}
|
|
1302
1339
|
async render(ctx) {
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1340
|
+
const container = document.createElement("div");
|
|
1341
|
+
container.className = "fp-pdf-container";
|
|
1342
|
+
container.style.width = "100%";
|
|
1343
|
+
container.style.height = "100%";
|
|
1344
|
+
container.style.overflow = "auto";
|
|
1345
|
+
container.style.display = "flex";
|
|
1346
|
+
container.style.flexDirection = "column";
|
|
1347
|
+
container.style.alignItems = "center";
|
|
1348
|
+
container.style.padding = "24px 16px";
|
|
1349
|
+
container.style.backgroundColor = "#0f172a";
|
|
1350
|
+
container.style.boxSizing = "border-box";
|
|
1351
|
+
container.style.position = "relative";
|
|
1352
|
+
const pageCard = document.createElement("div");
|
|
1353
|
+
pageCard.className = "fp-pdf-page-card";
|
|
1354
|
+
pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
|
|
1355
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
1356
|
+
pageCard.style.borderRadius = "4px";
|
|
1357
|
+
pageCard.style.overflow = "hidden";
|
|
1358
|
+
pageCard.style.lineHeight = "0";
|
|
1359
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
1360
|
+
pageCard.style.position = "relative";
|
|
1361
|
+
const canvas = document.createElement("canvas");
|
|
1362
|
+
pageCard.appendChild(canvas);
|
|
1363
|
+
container.appendChild(pageCard);
|
|
1364
|
+
const indicator = document.createElement("div");
|
|
1365
|
+
indicator.className = "fp-pdf-page-indicator";
|
|
1366
|
+
indicator.style.position = "sticky";
|
|
1367
|
+
indicator.style.bottom = "16px";
|
|
1368
|
+
indicator.style.marginTop = "16px";
|
|
1369
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1370
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1371
|
+
indicator.style.color = "#f8fafc";
|
|
1372
|
+
indicator.style.fontSize = "12px";
|
|
1373
|
+
indicator.style.fontWeight = "600";
|
|
1374
|
+
indicator.style.padding = "5px 14px";
|
|
1375
|
+
indicator.style.borderRadius = "20px";
|
|
1376
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1377
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1378
|
+
indicator.style.zIndex = "10";
|
|
1379
|
+
indicator.style.userSelect = "none";
|
|
1380
|
+
indicator.style.pointerEvents = "none";
|
|
1381
|
+
container.appendChild(indicator);
|
|
1382
|
+
ctx.container.appendChild(container);
|
|
1383
|
+
const loadingTask = pdfjsLib.getDocument({
|
|
1384
|
+
data: new Uint8Array(ctx.buffer),
|
|
1385
|
+
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
|
|
1386
|
+
cMapPacked: true,
|
|
1387
|
+
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
|
|
1388
|
+
});
|
|
1389
|
+
const pdfDoc = await loadingTask.promise;
|
|
1390
|
+
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1319
1391
|
let currentPage = 1;
|
|
1320
|
-
let
|
|
1392
|
+
let zoomScale = 1;
|
|
1321
1393
|
let rotation = 0;
|
|
1394
|
+
let currentRenderTask = null;
|
|
1395
|
+
const renderPage = async (pageNum) => {
|
|
1396
|
+
if (currentRenderTask) {
|
|
1397
|
+
try {
|
|
1398
|
+
currentRenderTask.cancel();
|
|
1399
|
+
} catch {
|
|
1400
|
+
}
|
|
1401
|
+
currentRenderTask = null;
|
|
1402
|
+
}
|
|
1403
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1404
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1405
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1406
|
+
const page = await pdfDoc.getPage(currentPage);
|
|
1407
|
+
const containerWidth = container.clientWidth || 900;
|
|
1408
|
+
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1409
|
+
const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
|
|
1410
|
+
const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
|
|
1411
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
1412
|
+
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
1413
|
+
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1414
|
+
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1415
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
1416
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
1417
|
+
const canvasCtx = canvas.getContext("2d");
|
|
1418
|
+
if (!canvasCtx) return;
|
|
1419
|
+
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
1420
|
+
currentRenderTask = page.render({
|
|
1421
|
+
canvasContext: canvasCtx,
|
|
1422
|
+
viewport
|
|
1423
|
+
});
|
|
1424
|
+
try {
|
|
1425
|
+
await currentRenderTask.promise;
|
|
1426
|
+
} catch (err) {
|
|
1427
|
+
if (err?.name !== "RenderingCancelledException") {
|
|
1428
|
+
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1429
|
+
}
|
|
1430
|
+
} finally {
|
|
1431
|
+
currentRenderTask = null;
|
|
1432
|
+
}
|
|
1433
|
+
};
|
|
1434
|
+
await renderPage(1);
|
|
1322
1435
|
const cleanup = () => {
|
|
1323
|
-
|
|
1324
|
-
|
|
1436
|
+
if (currentRenderTask) {
|
|
1437
|
+
try {
|
|
1438
|
+
currentRenderTask.cancel();
|
|
1439
|
+
} catch {
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
try {
|
|
1443
|
+
pdfDoc.destroy();
|
|
1444
|
+
} catch {
|
|
1445
|
+
}
|
|
1446
|
+
container.remove();
|
|
1325
1447
|
ctx.container.innerHTML = "";
|
|
1326
1448
|
};
|
|
1327
1449
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1328
|
-
|
|
1450
|
+
const instance = {
|
|
1329
1451
|
destroy: cleanup,
|
|
1330
1452
|
zoomIn: () => {
|
|
1331
|
-
|
|
1332
|
-
|
|
1453
|
+
zoomScale = Math.min(3.5, zoomScale + 0.2);
|
|
1454
|
+
renderPage(currentPage);
|
|
1333
1455
|
},
|
|
1334
1456
|
zoomOut: () => {
|
|
1335
|
-
|
|
1336
|
-
|
|
1457
|
+
zoomScale = Math.max(0.3, zoomScale - 0.2);
|
|
1458
|
+
renderPage(currentPage);
|
|
1337
1459
|
},
|
|
1338
|
-
getZoom: () =>
|
|
1460
|
+
getZoom: () => zoomScale,
|
|
1339
1461
|
setZoom: (level) => {
|
|
1340
|
-
|
|
1341
|
-
|
|
1462
|
+
zoomScale = Math.max(0.3, Math.min(3.5, level));
|
|
1463
|
+
renderPage(currentPage);
|
|
1342
1464
|
},
|
|
1343
1465
|
fitToPage: () => {
|
|
1344
|
-
|
|
1345
|
-
|
|
1466
|
+
zoomScale = 1;
|
|
1467
|
+
renderPage(currentPage);
|
|
1346
1468
|
},
|
|
1347
1469
|
rotateCW: () => {
|
|
1348
1470
|
rotation = (rotation + 90) % 360;
|
|
1349
|
-
|
|
1471
|
+
renderPage(currentPage);
|
|
1350
1472
|
},
|
|
1351
1473
|
rotateCCW: () => {
|
|
1352
1474
|
rotation = (rotation - 90 + 360) % 360;
|
|
1353
|
-
|
|
1475
|
+
renderPage(currentPage);
|
|
1354
1476
|
},
|
|
1355
1477
|
getRotation: () => rotation,
|
|
1478
|
+
getPageCount: () => totalPages,
|
|
1479
|
+
getCurrentPage: () => currentPage,
|
|
1356
1480
|
goToPage: (page) => {
|
|
1357
|
-
|
|
1358
|
-
iframe.src = `${url}#page=${page}`;
|
|
1481
|
+
renderPage(page);
|
|
1359
1482
|
},
|
|
1360
|
-
getCurrentPage: () => currentPage,
|
|
1361
1483
|
download: () => {
|
|
1484
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1485
|
+
const url = URL.createObjectURL(blob);
|
|
1362
1486
|
const a = document.createElement("a");
|
|
1363
1487
|
a.href = url;
|
|
1364
1488
|
a.download = ctx.metadata.name || "document.pdf";
|
|
1365
1489
|
a.click();
|
|
1490
|
+
URL.revokeObjectURL(url);
|
|
1366
1491
|
},
|
|
1367
1492
|
print: () => {
|
|
1368
|
-
|
|
1493
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1494
|
+
const url = URL.createObjectURL(blob);
|
|
1495
|
+
const hiddenIframe = document.createElement("iframe");
|
|
1496
|
+
hiddenIframe.style.position = "fixed";
|
|
1497
|
+
hiddenIframe.style.right = "0";
|
|
1498
|
+
hiddenIframe.style.bottom = "0";
|
|
1499
|
+
hiddenIframe.style.width = "0";
|
|
1500
|
+
hiddenIframe.style.height = "0";
|
|
1501
|
+
hiddenIframe.style.border = "0";
|
|
1502
|
+
document.body.appendChild(hiddenIframe);
|
|
1503
|
+
hiddenIframe.src = url;
|
|
1504
|
+
hiddenIframe.onload = () => {
|
|
1505
|
+
setTimeout(() => {
|
|
1506
|
+
hiddenIframe.contentWindow?.print();
|
|
1507
|
+
setTimeout(() => {
|
|
1508
|
+
hiddenIframe.remove();
|
|
1509
|
+
URL.revokeObjectURL(url);
|
|
1510
|
+
}, 1e3);
|
|
1511
|
+
}, 300);
|
|
1512
|
+
};
|
|
1369
1513
|
},
|
|
1370
1514
|
getThumbnails: async () => {
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1515
|
+
const thumbnails = [];
|
|
1516
|
+
const count = Math.min(totalPages, 50);
|
|
1517
|
+
for (let i = 1; i <= count; i++) {
|
|
1518
|
+
thumbnails.push({
|
|
1519
|
+
index: i,
|
|
1520
|
+
label: `Page ${i}`,
|
|
1521
|
+
render: async (thumbCanvas) => {
|
|
1522
|
+
try {
|
|
1523
|
+
const p = await pdfDoc.getPage(i);
|
|
1524
|
+
const baseVp = p.getViewport({ scale: 1 });
|
|
1525
|
+
const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
|
|
1526
|
+
const thumbVp = p.getViewport({ scale: thumbScale });
|
|
1527
|
+
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1528
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
1529
|
+
if (tCtx) {
|
|
1530
|
+
await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
|
|
1531
|
+
}
|
|
1532
|
+
} catch (e) {
|
|
1533
|
+
console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
|
|
1383
1534
|
}
|
|
1384
1535
|
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1536
|
+
});
|
|
1537
|
+
}
|
|
1538
|
+
return thumbnails;
|
|
1387
1539
|
}
|
|
1388
1540
|
};
|
|
1541
|
+
return instance;
|
|
1389
1542
|
}
|
|
1390
1543
|
};
|
|
1391
1544
|
function pdfPlugin() {
|
|
@@ -1712,7 +1865,31 @@ var DocxPlugin = class {
|
|
|
1712
1865
|
return this.mimeTypes.includes(mime || "");
|
|
1713
1866
|
}
|
|
1714
1867
|
getToolbarActions(instance) {
|
|
1715
|
-
|
|
1868
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1869
|
+
const actions = [];
|
|
1870
|
+
if (totalPages > 1) {
|
|
1871
|
+
actions.push({
|
|
1872
|
+
id: "page-nav",
|
|
1873
|
+
icon: "",
|
|
1874
|
+
label: "Page Navigation",
|
|
1875
|
+
type: "page-nav",
|
|
1876
|
+
group: "navigation",
|
|
1877
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
1878
|
+
max: totalPages,
|
|
1879
|
+
execute: (action, page) => {
|
|
1880
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1881
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1882
|
+
if (action === "prev") {
|
|
1883
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1884
|
+
} else if (action === "next") {
|
|
1885
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1886
|
+
} else if (typeof page === "number") {
|
|
1887
|
+
instance.goToPage?.(page);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
actions.push(
|
|
1716
1893
|
{
|
|
1717
1894
|
id: "zoom-out",
|
|
1718
1895
|
icon: "zoom-out",
|
|
@@ -1753,7 +1930,8 @@ var DocxPlugin = class {
|
|
|
1753
1930
|
group: "actions",
|
|
1754
1931
|
execute: () => instance.print?.()
|
|
1755
1932
|
}
|
|
1756
|
-
|
|
1933
|
+
);
|
|
1934
|
+
return actions;
|
|
1757
1935
|
}
|
|
1758
1936
|
async render(ctx) {
|
|
1759
1937
|
const wrapper = document.createElement("div");
|
|
@@ -1829,7 +2007,51 @@ var DocxPlugin = class {
|
|
|
1829
2007
|
}
|
|
1830
2008
|
}
|
|
1831
2009
|
}
|
|
2010
|
+
const sections = wrapper.querySelectorAll("section.docx");
|
|
2011
|
+
const cards = wrapper.querySelectorAll(".fp-docx-page-card");
|
|
2012
|
+
const pageElements = sections.length > 0 ? sections : cards;
|
|
2013
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
2014
|
+
let currentPage = 1;
|
|
2015
|
+
let indicator = null;
|
|
2016
|
+
if (totalPages > 1) {
|
|
2017
|
+
indicator = document.createElement("div");
|
|
2018
|
+
indicator.className = "fp-docx-page-indicator";
|
|
2019
|
+
indicator.style.position = "sticky";
|
|
2020
|
+
indicator.style.bottom = "16px";
|
|
2021
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2022
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2023
|
+
indicator.style.color = "#f8fafc";
|
|
2024
|
+
indicator.style.fontSize = "12px";
|
|
2025
|
+
indicator.style.fontWeight = "600";
|
|
2026
|
+
indicator.style.padding = "5px 14px";
|
|
2027
|
+
indicator.style.borderRadius = "20px";
|
|
2028
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2029
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2030
|
+
indicator.style.zIndex = "10";
|
|
2031
|
+
indicator.style.userSelect = "none";
|
|
2032
|
+
indicator.style.pointerEvents = "none";
|
|
2033
|
+
indicator.style.textAlign = "center";
|
|
2034
|
+
indicator.style.width = "fit-content";
|
|
2035
|
+
indicator.style.margin = "16px auto 0";
|
|
2036
|
+
ctx.container.appendChild(indicator);
|
|
2037
|
+
}
|
|
2038
|
+
const showPage = (pageNum) => {
|
|
2039
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2040
|
+
if (pageElements.length > 1) {
|
|
2041
|
+
pageElements.forEach((sec, idx) => {
|
|
2042
|
+
sec.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
2043
|
+
});
|
|
2044
|
+
}
|
|
2045
|
+
if (indicator) {
|
|
2046
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2047
|
+
}
|
|
2048
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2049
|
+
};
|
|
2050
|
+
if (totalPages > 1) {
|
|
2051
|
+
showPage(1);
|
|
2052
|
+
}
|
|
1832
2053
|
const cleanup = () => {
|
|
2054
|
+
indicator?.remove();
|
|
1833
2055
|
for (const url of createdBlobUrls) {
|
|
1834
2056
|
URL.revokeObjectURL(url);
|
|
1835
2057
|
}
|
|
@@ -1840,6 +2062,11 @@ var DocxPlugin = class {
|
|
|
1840
2062
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1841
2063
|
return {
|
|
1842
2064
|
destroy: cleanup,
|
|
2065
|
+
getPageCount: () => totalPages,
|
|
2066
|
+
getCurrentPage: () => currentPage,
|
|
2067
|
+
goToPage: (page) => {
|
|
2068
|
+
showPage(page);
|
|
2069
|
+
},
|
|
1843
2070
|
zoomIn: () => {
|
|
1844
2071
|
scale += 0.1;
|
|
1845
2072
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -2104,7 +2331,31 @@ var ExcelPlugin = class {
|
|
|
2104
2331
|
return this.mimeTypes.includes(mime || "");
|
|
2105
2332
|
}
|
|
2106
2333
|
getToolbarActions(instance) {
|
|
2107
|
-
|
|
2334
|
+
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2335
|
+
const actions = [];
|
|
2336
|
+
if (totalSheets > 1) {
|
|
2337
|
+
actions.push({
|
|
2338
|
+
id: "page-nav",
|
|
2339
|
+
icon: "",
|
|
2340
|
+
label: "Sheet Navigation",
|
|
2341
|
+
type: "page-nav",
|
|
2342
|
+
group: "navigation",
|
|
2343
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2344
|
+
max: totalSheets,
|
|
2345
|
+
execute: (action, sheet) => {
|
|
2346
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2347
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2348
|
+
if (action === "prev") {
|
|
2349
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2350
|
+
} else if (action === "next") {
|
|
2351
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2352
|
+
} else if (typeof sheet === "number") {
|
|
2353
|
+
instance.goToPage?.(sheet);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
actions.push(
|
|
2108
2359
|
{
|
|
2109
2360
|
id: "zoom-out",
|
|
2110
2361
|
icon: "zoom-out",
|
|
@@ -2125,16 +2376,6 @@ var ExcelPlugin = class {
|
|
|
2125
2376
|
instance.zoomIn?.();
|
|
2126
2377
|
}
|
|
2127
2378
|
},
|
|
2128
|
-
{
|
|
2129
|
-
id: "page-nav",
|
|
2130
|
-
icon: "page-nav",
|
|
2131
|
-
label: "Sheet Navigation",
|
|
2132
|
-
type: "page-nav",
|
|
2133
|
-
group: "navigation",
|
|
2134
|
-
execute: (sheet) => {
|
|
2135
|
-
if (typeof sheet === "number") instance.goToPage?.(sheet);
|
|
2136
|
-
}
|
|
2137
|
-
},
|
|
2138
2379
|
{
|
|
2139
2380
|
id: "download",
|
|
2140
2381
|
icon: "download",
|
|
@@ -2155,7 +2396,8 @@ var ExcelPlugin = class {
|
|
|
2155
2396
|
instance.print?.();
|
|
2156
2397
|
}
|
|
2157
2398
|
}
|
|
2158
|
-
|
|
2399
|
+
);
|
|
2400
|
+
return actions;
|
|
2159
2401
|
}
|
|
2160
2402
|
async render(ctx) {
|
|
2161
2403
|
const container = document.createElement("div");
|
|
@@ -2239,6 +2481,7 @@ var ExcelPlugin = class {
|
|
|
2239
2481
|
b.style.fontWeight = "normal";
|
|
2240
2482
|
}
|
|
2241
2483
|
});
|
|
2484
|
+
ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
|
|
2242
2485
|
};
|
|
2243
2486
|
if (sheetNames.length > 0) {
|
|
2244
2487
|
sheetNames.forEach((name, idx) => {
|
|
@@ -2488,7 +2731,31 @@ var CodePlugin = class {
|
|
|
2488
2731
|
return false;
|
|
2489
2732
|
}
|
|
2490
2733
|
getToolbarActions(instance) {
|
|
2491
|
-
|
|
2734
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2735
|
+
const actions = [];
|
|
2736
|
+
if (totalPages > 1) {
|
|
2737
|
+
actions.push({
|
|
2738
|
+
id: "page-nav",
|
|
2739
|
+
icon: "",
|
|
2740
|
+
label: "Page Navigation",
|
|
2741
|
+
type: "page-nav",
|
|
2742
|
+
group: "navigation",
|
|
2743
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2744
|
+
max: totalPages,
|
|
2745
|
+
execute: (action, page) => {
|
|
2746
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2747
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2748
|
+
if (action === "prev") {
|
|
2749
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2750
|
+
} else if (action === "next") {
|
|
2751
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2752
|
+
} else if (typeof page === "number") {
|
|
2753
|
+
instance.goToPage?.(page);
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
}
|
|
2758
|
+
actions.push(
|
|
2492
2759
|
{
|
|
2493
2760
|
id: "zoom-out",
|
|
2494
2761
|
icon: "zoom-out",
|
|
@@ -2539,11 +2806,16 @@ var CodePlugin = class {
|
|
|
2539
2806
|
instance.print?.();
|
|
2540
2807
|
}
|
|
2541
2808
|
}
|
|
2542
|
-
|
|
2809
|
+
);
|
|
2810
|
+
return actions;
|
|
2543
2811
|
}
|
|
2544
2812
|
async render(ctx) {
|
|
2545
2813
|
const decoder = new TextDecoder("utf-8");
|
|
2546
|
-
const
|
|
2814
|
+
const fullText = decoder.decode(ctx.buffer);
|
|
2815
|
+
const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
|
|
2816
|
+
const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
2817
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
2818
|
+
let currentPage = 1;
|
|
2547
2819
|
const container = document.createElement("div");
|
|
2548
2820
|
container.style.width = "100%";
|
|
2549
2821
|
container.style.height = "100%";
|
|
@@ -2562,25 +2834,66 @@ var CodePlugin = class {
|
|
|
2562
2834
|
pre.style.wordBreak = "break-all";
|
|
2563
2835
|
const code = document.createElement("code");
|
|
2564
2836
|
const ext = (ctx.metadata.extension || "").replace(".", "");
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2837
|
+
const renderCodePage = (text) => {
|
|
2838
|
+
try {
|
|
2839
|
+
if (ext && hljs.getLanguage(ext)) {
|
|
2840
|
+
code.innerHTML = hljs.highlight(text, { language: ext }).value;
|
|
2841
|
+
} else {
|
|
2842
|
+
code.innerHTML = hljs.highlightAuto(text).value;
|
|
2843
|
+
}
|
|
2844
|
+
} catch {
|
|
2845
|
+
code.textContent = text;
|
|
2570
2846
|
}
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
}
|
|
2847
|
+
};
|
|
2848
|
+
renderCodePage(rawPages[0] || fullText);
|
|
2574
2849
|
pre.appendChild(code);
|
|
2575
2850
|
container.appendChild(pre);
|
|
2851
|
+
let indicator = null;
|
|
2852
|
+
if (totalPages > 1) {
|
|
2853
|
+
indicator = document.createElement("div");
|
|
2854
|
+
indicator.className = "fp-code-page-indicator";
|
|
2855
|
+
indicator.style.position = "sticky";
|
|
2856
|
+
indicator.style.bottom = "16px";
|
|
2857
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2858
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2859
|
+
indicator.style.color = "#f8fafc";
|
|
2860
|
+
indicator.style.fontSize = "12px";
|
|
2861
|
+
indicator.style.fontWeight = "600";
|
|
2862
|
+
indicator.style.padding = "5px 14px";
|
|
2863
|
+
indicator.style.borderRadius = "20px";
|
|
2864
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2865
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2866
|
+
indicator.style.zIndex = "10";
|
|
2867
|
+
indicator.style.userSelect = "none";
|
|
2868
|
+
indicator.style.pointerEvents = "none";
|
|
2869
|
+
indicator.style.textAlign = "center";
|
|
2870
|
+
indicator.style.width = "fit-content";
|
|
2871
|
+
indicator.style.margin = "16px auto 0";
|
|
2872
|
+
container.appendChild(indicator);
|
|
2873
|
+
}
|
|
2874
|
+
const showPage = (pageNum) => {
|
|
2875
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2876
|
+
renderCodePage(rawPages[currentPage - 1] || fullText);
|
|
2877
|
+
if (indicator) {
|
|
2878
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2879
|
+
}
|
|
2880
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2881
|
+
};
|
|
2882
|
+
if (totalPages > 1) {
|
|
2883
|
+
showPage(1);
|
|
2884
|
+
}
|
|
2576
2885
|
ctx.container.appendChild(container);
|
|
2577
2886
|
const cleanup = () => {
|
|
2887
|
+
indicator?.remove();
|
|
2578
2888
|
container.remove();
|
|
2579
2889
|
ctx.container.innerHTML = "";
|
|
2580
2890
|
};
|
|
2581
2891
|
ctx.signal.addEventListener("abort", cleanup);
|
|
2582
2892
|
return {
|
|
2583
2893
|
destroy: cleanup,
|
|
2894
|
+
getPageCount: () => totalPages,
|
|
2895
|
+
getCurrentPage: () => currentPage,
|
|
2896
|
+
goToPage: (page) => showPage(page),
|
|
2584
2897
|
zoomIn: () => {
|
|
2585
2898
|
fontSize = Math.min(32, fontSize + 2);
|
|
2586
2899
|
pre.style.fontSize = `${fontSize}px`;
|
|
@@ -2608,7 +2921,7 @@ var CodePlugin = class {
|
|
|
2608
2921
|
window.print();
|
|
2609
2922
|
},
|
|
2610
2923
|
copy: () => {
|
|
2611
|
-
navigator.clipboard?.writeText(
|
|
2924
|
+
navigator.clipboard?.writeText(fullText);
|
|
2612
2925
|
}
|
|
2613
2926
|
};
|
|
2614
2927
|
}
|
|
@@ -3430,7 +3743,31 @@ var RtfPlugin = class {
|
|
|
3430
3743
|
return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
|
|
3431
3744
|
}
|
|
3432
3745
|
getToolbarActions(instance) {
|
|
3433
|
-
|
|
3746
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3747
|
+
const actions = [];
|
|
3748
|
+
if (totalPages > 1) {
|
|
3749
|
+
actions.push({
|
|
3750
|
+
id: "page-nav",
|
|
3751
|
+
icon: "",
|
|
3752
|
+
label: "Page Navigation",
|
|
3753
|
+
type: "page-nav",
|
|
3754
|
+
group: "navigation",
|
|
3755
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3756
|
+
max: totalPages,
|
|
3757
|
+
execute: (action, page) => {
|
|
3758
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3759
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3760
|
+
if (action === "prev") {
|
|
3761
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3762
|
+
} else if (action === "next") {
|
|
3763
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3764
|
+
} else if (typeof page === "number") {
|
|
3765
|
+
instance.goToPage?.(page);
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3770
|
+
actions.push(
|
|
3434
3771
|
{
|
|
3435
3772
|
id: "zoom-out",
|
|
3436
3773
|
icon: "zoom-out",
|
|
@@ -3471,7 +3808,8 @@ var RtfPlugin = class {
|
|
|
3471
3808
|
group: "actions",
|
|
3472
3809
|
execute: () => instance.print?.()
|
|
3473
3810
|
}
|
|
3474
|
-
|
|
3811
|
+
);
|
|
3812
|
+
return actions;
|
|
3475
3813
|
}
|
|
3476
3814
|
async render(ctx) {
|
|
3477
3815
|
const wrapper = document.createElement("div");
|
|
@@ -3490,28 +3828,82 @@ var RtfPlugin = class {
|
|
|
3490
3828
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
3491
3829
|
ctx.container.appendChild(wrapper);
|
|
3492
3830
|
let scale = 1;
|
|
3831
|
+
let pageElements = [];
|
|
3493
3832
|
try {
|
|
3494
3833
|
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3495
3834
|
RTFJS.loggingEnabled(false);
|
|
3496
3835
|
}
|
|
3497
3836
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3498
3837
|
const htmlElements = await doc.render();
|
|
3499
|
-
|
|
3838
|
+
pageElements = htmlElements;
|
|
3839
|
+
for (let i = 0; i < htmlElements.length; i++) {
|
|
3840
|
+
const el = htmlElements[i];
|
|
3841
|
+
el.style.display = i === 0 ? "block" : "none";
|
|
3500
3842
|
wrapper.appendChild(el);
|
|
3501
3843
|
}
|
|
3502
3844
|
} catch (err) {
|
|
3503
3845
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
3504
3846
|
const text = new TextDecoder("latin1").decode(ctx.buffer);
|
|
3505
3847
|
const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
|
|
3506
|
-
|
|
3848
|
+
const pre = document.createElement("pre");
|
|
3849
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
3850
|
+
pre.style.fontFamily = "serif";
|
|
3851
|
+
pre.style.color = "#333";
|
|
3852
|
+
pre.textContent = clean;
|
|
3853
|
+
wrapper.appendChild(pre);
|
|
3854
|
+
pageElements = [pre];
|
|
3855
|
+
}
|
|
3856
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
3857
|
+
let currentPage = 1;
|
|
3858
|
+
let indicator = null;
|
|
3859
|
+
if (totalPages > 1) {
|
|
3860
|
+
indicator = document.createElement("div");
|
|
3861
|
+
indicator.className = "fp-rtf-page-indicator";
|
|
3862
|
+
indicator.style.position = "sticky";
|
|
3863
|
+
indicator.style.bottom = "16px";
|
|
3864
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3865
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
3866
|
+
indicator.style.color = "#f8fafc";
|
|
3867
|
+
indicator.style.fontSize = "12px";
|
|
3868
|
+
indicator.style.fontWeight = "600";
|
|
3869
|
+
indicator.style.padding = "5px 14px";
|
|
3870
|
+
indicator.style.borderRadius = "20px";
|
|
3871
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3872
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3873
|
+
indicator.style.zIndex = "10";
|
|
3874
|
+
indicator.style.userSelect = "none";
|
|
3875
|
+
indicator.style.pointerEvents = "none";
|
|
3876
|
+
indicator.style.textAlign = "center";
|
|
3877
|
+
indicator.style.width = "fit-content";
|
|
3878
|
+
indicator.style.margin = "16px auto 0";
|
|
3879
|
+
ctx.container.appendChild(indicator);
|
|
3880
|
+
}
|
|
3881
|
+
const showPage = (pageNum) => {
|
|
3882
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3883
|
+
if (totalPages > 1) {
|
|
3884
|
+
pageElements.forEach((el, idx) => {
|
|
3885
|
+
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
3886
|
+
});
|
|
3887
|
+
}
|
|
3888
|
+
if (indicator) {
|
|
3889
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3890
|
+
}
|
|
3891
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3892
|
+
};
|
|
3893
|
+
if (totalPages > 1) {
|
|
3894
|
+
showPage(1);
|
|
3507
3895
|
}
|
|
3508
3896
|
const cleanup = () => {
|
|
3897
|
+
indicator?.remove();
|
|
3509
3898
|
wrapper.remove();
|
|
3510
3899
|
ctx.container.innerHTML = "";
|
|
3511
3900
|
};
|
|
3512
3901
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3513
3902
|
return {
|
|
3514
3903
|
destroy: cleanup,
|
|
3904
|
+
getPageCount: () => totalPages,
|
|
3905
|
+
getCurrentPage: () => currentPage,
|
|
3906
|
+
goToPage: (page) => showPage(page),
|
|
3515
3907
|
zoomIn: () => {
|
|
3516
3908
|
scale += 0.1;
|
|
3517
3909
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -3699,41 +4091,41 @@ var OpenDocumentPlugin = class {
|
|
|
3699
4091
|
}
|
|
3700
4092
|
getToolbarActions(instance) {
|
|
3701
4093
|
const isPresentation = instance.isPresentation;
|
|
4094
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3702
4095
|
const actions = [];
|
|
3703
|
-
if (isPresentation) {
|
|
3704
|
-
|
|
3705
|
-
{
|
|
4096
|
+
if (isPresentation || totalPages > 1) {
|
|
4097
|
+
if (isPresentation) {
|
|
4098
|
+
actions.push({
|
|
3706
4099
|
id: "thumbnails",
|
|
3707
4100
|
icon: "thumbnails",
|
|
3708
4101
|
label: "Slide Thumbnails",
|
|
3709
4102
|
type: "button",
|
|
3710
4103
|
group: "navigation",
|
|
3711
4104
|
execute: () => instance.toggleThumbnails?.()
|
|
3712
|
-
}
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
}
|
|
4105
|
+
});
|
|
4106
|
+
}
|
|
4107
|
+
actions.push({
|
|
4108
|
+
id: "page-nav",
|
|
4109
|
+
icon: "",
|
|
4110
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
4111
|
+
type: "page-nav",
|
|
4112
|
+
group: "navigation",
|
|
4113
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4114
|
+
max: totalPages,
|
|
4115
|
+
execute: (action, page) => {
|
|
4116
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4117
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4118
|
+
if (action === "prev") {
|
|
4119
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4120
|
+
} else if (action === "next") {
|
|
4121
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4122
|
+
} else if (typeof page === "number") {
|
|
4123
|
+
instance.goToPage?.(page);
|
|
4124
|
+
} else if (typeof action === "number") {
|
|
4125
|
+
instance.goToPage?.(action);
|
|
3734
4126
|
}
|
|
3735
4127
|
}
|
|
3736
|
-
);
|
|
4128
|
+
});
|
|
3737
4129
|
}
|
|
3738
4130
|
actions.push(
|
|
3739
4131
|
{
|
|
@@ -4116,7 +4508,31 @@ var DocPlugin = class {
|
|
|
4116
4508
|
return this.mimeTypes.includes(mime || "");
|
|
4117
4509
|
}
|
|
4118
4510
|
getToolbarActions(instance) {
|
|
4119
|
-
|
|
4511
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4512
|
+
const actions = [];
|
|
4513
|
+
if (totalPages > 1) {
|
|
4514
|
+
actions.push({
|
|
4515
|
+
id: "page-nav",
|
|
4516
|
+
icon: "",
|
|
4517
|
+
label: "Page Navigation",
|
|
4518
|
+
type: "page-nav",
|
|
4519
|
+
group: "navigation",
|
|
4520
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4521
|
+
max: totalPages,
|
|
4522
|
+
execute: (action, page) => {
|
|
4523
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4524
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4525
|
+
if (action === "prev") {
|
|
4526
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4527
|
+
} else if (action === "next") {
|
|
4528
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4529
|
+
} else if (typeof page === "number") {
|
|
4530
|
+
instance.goToPage?.(page);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
});
|
|
4534
|
+
}
|
|
4535
|
+
actions.push(
|
|
4120
4536
|
{
|
|
4121
4537
|
id: "zoom-out",
|
|
4122
4538
|
icon: "zoom-out",
|
|
@@ -4165,7 +4581,8 @@ var DocPlugin = class {
|
|
|
4165
4581
|
group: "actions",
|
|
4166
4582
|
execute: () => instance.print?.()
|
|
4167
4583
|
}
|
|
4168
|
-
|
|
4584
|
+
);
|
|
4585
|
+
return actions;
|
|
4169
4586
|
}
|
|
4170
4587
|
async render(ctx) {
|
|
4171
4588
|
const container = document.createElement("div");
|
|
@@ -4192,6 +4609,7 @@ var DocPlugin = class {
|
|
|
4192
4609
|
ctx.container.appendChild(container);
|
|
4193
4610
|
let scale = 1;
|
|
4194
4611
|
let extractedRawText = "";
|
|
4612
|
+
let isFallback = false;
|
|
4195
4613
|
try {
|
|
4196
4614
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4197
4615
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
@@ -4205,26 +4623,89 @@ var DocPlugin = class {
|
|
|
4205
4623
|
const tableStream = cfbf.readStream(tableName);
|
|
4206
4624
|
const text = this.extractDocText(wordDocStream, tableStream);
|
|
4207
4625
|
extractedRawText = text;
|
|
4208
|
-
wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
|
|
4209
4626
|
} catch (err) {
|
|
4210
4627
|
console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
|
|
4211
4628
|
const fallback = this.heuristicTextExtraction(ctx.buffer);
|
|
4212
4629
|
extractedRawText = fallback;
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4630
|
+
isFallback = true;
|
|
4631
|
+
}
|
|
4632
|
+
const rawPages = this.splitIntoPages(extractedRawText);
|
|
4633
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
4634
|
+
let currentPage = 1;
|
|
4635
|
+
wrapper.innerHTML = "";
|
|
4636
|
+
const pageCards = [];
|
|
4637
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4638
|
+
const pageCard = document.createElement("div");
|
|
4639
|
+
pageCard.className = "fp-doc-page-card";
|
|
4640
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
4641
|
+
pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
|
|
4642
|
+
pageCard.style.borderRadius = "4px";
|
|
4643
|
+
pageCard.style.padding = "56px 48px";
|
|
4644
|
+
pageCard.style.minHeight = "100%";
|
|
4645
|
+
pageCard.style.display = i === 0 ? "block" : "none";
|
|
4646
|
+
if (isFallback && i === 0) {
|
|
4647
|
+
pageCard.innerHTML = `
|
|
4648
|
+
<div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
|
|
4649
|
+
<h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
|
|
4650
|
+
<span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
|
|
4651
|
+
</div>
|
|
4652
|
+
${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
|
|
4653
|
+
`;
|
|
4654
|
+
} else {
|
|
4655
|
+
pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
|
|
4656
|
+
}
|
|
4657
|
+
wrapper.appendChild(pageCard);
|
|
4658
|
+
pageCards.push(pageCard);
|
|
4659
|
+
}
|
|
4660
|
+
let indicator = null;
|
|
4661
|
+
if (totalPages > 1) {
|
|
4662
|
+
indicator = document.createElement("div");
|
|
4663
|
+
indicator.className = "fp-doc-page-indicator";
|
|
4664
|
+
indicator.style.position = "sticky";
|
|
4665
|
+
indicator.style.bottom = "16px";
|
|
4666
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4667
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4668
|
+
indicator.style.color = "#f8fafc";
|
|
4669
|
+
indicator.style.fontSize = "12px";
|
|
4670
|
+
indicator.style.fontWeight = "600";
|
|
4671
|
+
indicator.style.padding = "5px 14px";
|
|
4672
|
+
indicator.style.borderRadius = "20px";
|
|
4673
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4674
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4675
|
+
indicator.style.zIndex = "10";
|
|
4676
|
+
indicator.style.userSelect = "none";
|
|
4677
|
+
indicator.style.pointerEvents = "none";
|
|
4678
|
+
indicator.style.textAlign = "center";
|
|
4679
|
+
indicator.style.width = "fit-content";
|
|
4680
|
+
indicator.style.margin = "16px auto 0";
|
|
4681
|
+
container.appendChild(indicator);
|
|
4682
|
+
}
|
|
4683
|
+
const showPage = (pageNum) => {
|
|
4684
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4685
|
+
if (totalPages > 1) {
|
|
4686
|
+
pageCards.forEach((card, idx) => {
|
|
4687
|
+
card.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4688
|
+
});
|
|
4689
|
+
}
|
|
4690
|
+
if (indicator) {
|
|
4691
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4692
|
+
}
|
|
4693
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4694
|
+
};
|
|
4695
|
+
if (totalPages > 1) {
|
|
4696
|
+
showPage(1);
|
|
4220
4697
|
}
|
|
4221
4698
|
const cleanup = () => {
|
|
4699
|
+
indicator?.remove();
|
|
4222
4700
|
container.remove();
|
|
4223
4701
|
ctx.container.innerHTML = "";
|
|
4224
4702
|
};
|
|
4225
4703
|
ctx.signal.addEventListener("abort", cleanup);
|
|
4226
4704
|
return {
|
|
4227
4705
|
destroy: cleanup,
|
|
4706
|
+
getPageCount: () => totalPages,
|
|
4707
|
+
getCurrentPage: () => currentPage,
|
|
4708
|
+
goToPage: (page) => showPage(page),
|
|
4228
4709
|
zoomIn: () => {
|
|
4229
4710
|
scale += 0.1;
|
|
4230
4711
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -4356,11 +4837,16 @@ var DocPlugin = class {
|
|
|
4356
4837
|
heuristicTextExtraction(buffer) {
|
|
4357
4838
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
4358
4839
|
}
|
|
4840
|
+
splitIntoPages(text) {
|
|
4841
|
+
if (!text) return [""];
|
|
4842
|
+
const parts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
4843
|
+
return parts.length > 0 ? parts : [text];
|
|
4844
|
+
}
|
|
4359
4845
|
/**
|
|
4360
4846
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
4361
4847
|
*/
|
|
4362
4848
|
formatDocToHtml(text, filename) {
|
|
4363
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").
|
|
4849
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
4364
4850
|
let html = "";
|
|
4365
4851
|
let inList = false;
|
|
4366
4852
|
for (const rawLine of lines) {
|