@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/react.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { memo, forwardRef, useRef, useImperativeHandle, useEffect } from 'react';
|
|
2
2
|
import DOMPurify2 from 'dompurify';
|
|
3
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
3
4
|
import * as docx from 'docx-preview';
|
|
4
5
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
5
6
|
import * as XLSX from 'xlsx';
|
|
@@ -480,11 +481,21 @@ var ToolbarController = class {
|
|
|
480
481
|
el;
|
|
481
482
|
toolbarEl;
|
|
482
483
|
actions = [];
|
|
484
|
+
pageInputEl = null;
|
|
485
|
+
pageLabelEl = null;
|
|
483
486
|
constructor(container) {
|
|
484
487
|
this.el = container;
|
|
485
488
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
486
489
|
this.el.appendChild(this.toolbarEl);
|
|
487
490
|
}
|
|
491
|
+
setPage(page, max) {
|
|
492
|
+
if (this.pageInputEl) {
|
|
493
|
+
this.pageInputEl.value = page.toString();
|
|
494
|
+
}
|
|
495
|
+
if (max !== void 0 && this.pageLabelEl) {
|
|
496
|
+
this.pageLabelEl.textContent = ` / ${max}`;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
488
499
|
update(actions) {
|
|
489
500
|
this.actions = actions;
|
|
490
501
|
this.render();
|
|
@@ -527,6 +538,7 @@ var ToolbarController = class {
|
|
|
527
538
|
if (action.type === "separator") {
|
|
528
539
|
groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
|
|
529
540
|
} else if (action.type === "page-nav") {
|
|
541
|
+
const max = action.max ?? 1;
|
|
530
542
|
const prevBtn = this.createButton(
|
|
531
543
|
"prev",
|
|
532
544
|
ICON_PAGE_PREV,
|
|
@@ -545,7 +557,6 @@ var ToolbarController = class {
|
|
|
545
557
|
"Next Page",
|
|
546
558
|
() => {
|
|
547
559
|
const cur = parseInt(input.value, 10) || 1;
|
|
548
|
-
const max = action.max ?? 1;
|
|
549
560
|
if (cur < max) {
|
|
550
561
|
input.value = (cur + 1).toString();
|
|
551
562
|
action.execute("next", cur + 1);
|
|
@@ -557,15 +568,18 @@ var ToolbarController = class {
|
|
|
557
568
|
type: "number",
|
|
558
569
|
value: (action.value ?? 1).toString(),
|
|
559
570
|
min: "1",
|
|
560
|
-
max:
|
|
571
|
+
max: max.toString()
|
|
561
572
|
});
|
|
562
573
|
input.addEventListener("change", () => {
|
|
563
|
-
|
|
564
|
-
if (
|
|
565
|
-
|
|
566
|
-
|
|
574
|
+
let val = parseInt(input.value, 10);
|
|
575
|
+
if (isNaN(val)) val = 1;
|
|
576
|
+
val = Math.max(1, Math.min(max, val));
|
|
577
|
+
input.value = val.toString();
|
|
578
|
+
action.execute("go", val);
|
|
567
579
|
});
|
|
568
|
-
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${
|
|
580
|
+
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
581
|
+
this.pageInputEl = input;
|
|
582
|
+
this.pageLabelEl = label;
|
|
569
583
|
groupEl.appendChild(prevBtn);
|
|
570
584
|
groupEl.appendChild(input);
|
|
571
585
|
groupEl.appendChild(label);
|
|
@@ -743,7 +757,13 @@ var FilePreviewViewer = class {
|
|
|
743
757
|
buffer,
|
|
744
758
|
options,
|
|
745
759
|
signal,
|
|
746
|
-
emit: (event, payload) =>
|
|
760
|
+
emit: (event, payload) => {
|
|
761
|
+
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
762
|
+
const total = payload.total ?? payload.totalPages;
|
|
763
|
+
this.toolbar?.setPage(payload.page, total);
|
|
764
|
+
}
|
|
765
|
+
this.eventEmitter.emit(event, payload);
|
|
766
|
+
}
|
|
747
767
|
});
|
|
748
768
|
this.activeInstance = instance;
|
|
749
769
|
this.hideLoading();
|
|
@@ -777,6 +797,7 @@ var FilePreviewViewer = class {
|
|
|
777
797
|
if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
|
|
778
798
|
this.thumbnailPanel.update(thumbnails, (index) => {
|
|
779
799
|
instance.goToPage?.(index + 1);
|
|
800
|
+
this.toolbar?.setPage(index + 1);
|
|
780
801
|
});
|
|
781
802
|
if (options.showThumbnails) {
|
|
782
803
|
this.thumbnailPanel.show();
|
|
@@ -902,9 +923,13 @@ var FilePreviewViewer = class {
|
|
|
902
923
|
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
903
924
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
904
925
|
this.activeInstance.goToPage?.(cur + 1);
|
|
926
|
+
const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
|
|
927
|
+
this.toolbar?.setPage(nextCur);
|
|
905
928
|
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
906
929
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
907
930
|
this.activeInstance.goToPage?.(Math.max(1, cur - 1));
|
|
931
|
+
const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
|
|
932
|
+
this.toolbar?.setPage(prevCur);
|
|
908
933
|
} else if (e.key === "+" || e.key === "=") {
|
|
909
934
|
this.activeInstance.zoomIn?.();
|
|
910
935
|
} else if (e.key === "-" || e.key === "_") {
|
|
@@ -1170,30 +1195,62 @@ var CfbfReader = class {
|
|
|
1170
1195
|
return result.subarray(0, targetSize);
|
|
1171
1196
|
}
|
|
1172
1197
|
};
|
|
1173
|
-
|
|
1174
|
-
|
|
1198
|
+
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1199
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1200
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1175
1203
|
var PdfPlugin = class {
|
|
1176
1204
|
id = "pdf";
|
|
1177
|
-
name = "PDF Preview";
|
|
1205
|
+
name = "PDF Document Preview";
|
|
1178
1206
|
extensions = [".pdf"];
|
|
1179
1207
|
mimeTypes = ["application/pdf"];
|
|
1180
1208
|
weight = 100;
|
|
1181
1209
|
supports(file) {
|
|
1182
1210
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1183
1211
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1184
|
-
|
|
1212
|
+
if (ext) return this.extensions.includes(ext);
|
|
1213
|
+
return this.mimeTypes.includes(mime || "");
|
|
1185
1214
|
}
|
|
1186
1215
|
getToolbarActions(instance) {
|
|
1216
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1217
|
+
const curPage = instance.getCurrentPage?.() ?? 1;
|
|
1187
1218
|
return [
|
|
1219
|
+
{
|
|
1220
|
+
id: "thumbnails",
|
|
1221
|
+
icon: "thumbnails",
|
|
1222
|
+
label: "Page Thumbnails",
|
|
1223
|
+
type: "button",
|
|
1224
|
+
group: "navigation",
|
|
1225
|
+
execute: () => instance.toggleThumbnails?.()
|
|
1226
|
+
},
|
|
1227
|
+
{
|
|
1228
|
+
id: "page-nav",
|
|
1229
|
+
icon: "",
|
|
1230
|
+
label: "Page Navigation",
|
|
1231
|
+
type: "page-nav",
|
|
1232
|
+
group: "navigation",
|
|
1233
|
+
value: curPage,
|
|
1234
|
+
max: totalPages,
|
|
1235
|
+
execute: (action, page) => {
|
|
1236
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1237
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1238
|
+
if (action === "prev") {
|
|
1239
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1240
|
+
} else if (action === "next") {
|
|
1241
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1242
|
+
} else if (typeof page === "number") {
|
|
1243
|
+
instance.goToPage?.(page);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
},
|
|
1188
1247
|
{
|
|
1189
1248
|
id: "zoom-out",
|
|
1190
1249
|
icon: "zoom-out",
|
|
1191
1250
|
label: "Zoom Out",
|
|
1192
1251
|
type: "button",
|
|
1193
1252
|
group: "zoom",
|
|
1194
|
-
execute: () =>
|
|
1195
|
-
instance.zoomOut?.();
|
|
1196
|
-
}
|
|
1253
|
+
execute: () => instance.zoomOut?.()
|
|
1197
1254
|
},
|
|
1198
1255
|
{
|
|
1199
1256
|
id: "zoom-in",
|
|
@@ -1201,9 +1258,7 @@ var PdfPlugin = class {
|
|
|
1201
1258
|
label: "Zoom In",
|
|
1202
1259
|
type: "button",
|
|
1203
1260
|
group: "zoom",
|
|
1204
|
-
execute: () =>
|
|
1205
|
-
instance.zoomIn?.();
|
|
1206
|
-
}
|
|
1261
|
+
execute: () => instance.zoomIn?.()
|
|
1207
1262
|
},
|
|
1208
1263
|
{
|
|
1209
1264
|
id: "fit-page",
|
|
@@ -1211,39 +1266,23 @@ var PdfPlugin = class {
|
|
|
1211
1266
|
label: "Fit to Page",
|
|
1212
1267
|
type: "button",
|
|
1213
1268
|
group: "zoom",
|
|
1214
|
-
execute: () =>
|
|
1215
|
-
instance.fitToPage?.();
|
|
1216
|
-
}
|
|
1269
|
+
execute: () => instance.fitToPage?.()
|
|
1217
1270
|
},
|
|
1218
1271
|
{
|
|
1219
1272
|
id: "rotate-cw",
|
|
1220
1273
|
icon: "rotate-cw",
|
|
1221
|
-
label: "Rotate",
|
|
1274
|
+
label: "Rotate Clockwise",
|
|
1222
1275
|
type: "button",
|
|
1223
1276
|
group: "view",
|
|
1224
|
-
execute: () =>
|
|
1225
|
-
instance.rotateCW?.();
|
|
1226
|
-
}
|
|
1227
|
-
},
|
|
1228
|
-
{
|
|
1229
|
-
id: "page-nav",
|
|
1230
|
-
icon: "page-nav",
|
|
1231
|
-
label: "Page Navigation",
|
|
1232
|
-
type: "page-nav",
|
|
1233
|
-
group: "navigation",
|
|
1234
|
-
execute: (page) => {
|
|
1235
|
-
if (typeof page === "number") instance.goToPage?.(page);
|
|
1236
|
-
}
|
|
1277
|
+
execute: () => instance.rotateCW?.()
|
|
1237
1278
|
},
|
|
1238
1279
|
{
|
|
1239
1280
|
id: "download",
|
|
1240
1281
|
icon: "download",
|
|
1241
|
-
label: "Download",
|
|
1282
|
+
label: "Download PDF",
|
|
1242
1283
|
type: "button",
|
|
1243
1284
|
group: "actions",
|
|
1244
|
-
execute: () =>
|
|
1245
|
-
instance.download?.();
|
|
1246
|
-
}
|
|
1285
|
+
execute: () => instance.download?.()
|
|
1247
1286
|
},
|
|
1248
1287
|
{
|
|
1249
1288
|
id: "print",
|
|
@@ -1251,99 +1290,213 @@ var PdfPlugin = class {
|
|
|
1251
1290
|
label: "Print",
|
|
1252
1291
|
type: "button",
|
|
1253
1292
|
group: "actions",
|
|
1254
|
-
execute: () =>
|
|
1255
|
-
instance.print?.();
|
|
1256
|
-
}
|
|
1293
|
+
execute: () => instance.print?.()
|
|
1257
1294
|
}
|
|
1258
1295
|
];
|
|
1259
1296
|
}
|
|
1260
1297
|
async render(ctx) {
|
|
1261
|
-
const
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1298
|
+
const container = document.createElement("div");
|
|
1299
|
+
container.className = "fp-pdf-container";
|
|
1300
|
+
container.style.width = "100%";
|
|
1301
|
+
container.style.height = "100%";
|
|
1302
|
+
container.style.overflow = "auto";
|
|
1303
|
+
container.style.display = "flex";
|
|
1304
|
+
container.style.flexDirection = "column";
|
|
1305
|
+
container.style.alignItems = "center";
|
|
1306
|
+
container.style.padding = "24px 16px";
|
|
1307
|
+
container.style.backgroundColor = "#0f172a";
|
|
1308
|
+
container.style.boxSizing = "border-box";
|
|
1309
|
+
container.style.position = "relative";
|
|
1310
|
+
const pageCard = document.createElement("div");
|
|
1311
|
+
pageCard.className = "fp-pdf-page-card";
|
|
1312
|
+
pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
|
|
1313
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
1314
|
+
pageCard.style.borderRadius = "4px";
|
|
1315
|
+
pageCard.style.overflow = "hidden";
|
|
1316
|
+
pageCard.style.lineHeight = "0";
|
|
1317
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
1318
|
+
pageCard.style.position = "relative";
|
|
1319
|
+
const canvas = document.createElement("canvas");
|
|
1320
|
+
pageCard.appendChild(canvas);
|
|
1321
|
+
container.appendChild(pageCard);
|
|
1322
|
+
const indicator = document.createElement("div");
|
|
1323
|
+
indicator.className = "fp-pdf-page-indicator";
|
|
1324
|
+
indicator.style.position = "sticky";
|
|
1325
|
+
indicator.style.bottom = "16px";
|
|
1326
|
+
indicator.style.marginTop = "16px";
|
|
1327
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1328
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1329
|
+
indicator.style.color = "#f8fafc";
|
|
1330
|
+
indicator.style.fontSize = "12px";
|
|
1331
|
+
indicator.style.fontWeight = "600";
|
|
1332
|
+
indicator.style.padding = "5px 14px";
|
|
1333
|
+
indicator.style.borderRadius = "20px";
|
|
1334
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1335
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1336
|
+
indicator.style.zIndex = "10";
|
|
1337
|
+
indicator.style.userSelect = "none";
|
|
1338
|
+
indicator.style.pointerEvents = "none";
|
|
1339
|
+
container.appendChild(indicator);
|
|
1340
|
+
ctx.container.appendChild(container);
|
|
1341
|
+
const loadingTask = pdfjsLib.getDocument({
|
|
1342
|
+
data: new Uint8Array(ctx.buffer),
|
|
1343
|
+
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
|
|
1344
|
+
cMapPacked: true,
|
|
1345
|
+
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
|
|
1346
|
+
});
|
|
1347
|
+
const pdfDoc = await loadingTask.promise;
|
|
1348
|
+
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1277
1349
|
let currentPage = 1;
|
|
1278
|
-
let
|
|
1350
|
+
let zoomScale = 1;
|
|
1279
1351
|
let rotation = 0;
|
|
1352
|
+
let currentRenderTask = null;
|
|
1353
|
+
const renderPage = async (pageNum) => {
|
|
1354
|
+
if (currentRenderTask) {
|
|
1355
|
+
try {
|
|
1356
|
+
currentRenderTask.cancel();
|
|
1357
|
+
} catch {
|
|
1358
|
+
}
|
|
1359
|
+
currentRenderTask = null;
|
|
1360
|
+
}
|
|
1361
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1362
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1363
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1364
|
+
const page = await pdfDoc.getPage(currentPage);
|
|
1365
|
+
const containerWidth = container.clientWidth || 900;
|
|
1366
|
+
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1367
|
+
const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
|
|
1368
|
+
const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
|
|
1369
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
1370
|
+
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
1371
|
+
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1372
|
+
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1373
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
1374
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
1375
|
+
const canvasCtx = canvas.getContext("2d");
|
|
1376
|
+
if (!canvasCtx) return;
|
|
1377
|
+
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
1378
|
+
currentRenderTask = page.render({
|
|
1379
|
+
canvasContext: canvasCtx,
|
|
1380
|
+
viewport
|
|
1381
|
+
});
|
|
1382
|
+
try {
|
|
1383
|
+
await currentRenderTask.promise;
|
|
1384
|
+
} catch (err) {
|
|
1385
|
+
if (err?.name !== "RenderingCancelledException") {
|
|
1386
|
+
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1387
|
+
}
|
|
1388
|
+
} finally {
|
|
1389
|
+
currentRenderTask = null;
|
|
1390
|
+
}
|
|
1391
|
+
};
|
|
1392
|
+
await renderPage(1);
|
|
1280
1393
|
const cleanup = () => {
|
|
1281
|
-
|
|
1282
|
-
|
|
1394
|
+
if (currentRenderTask) {
|
|
1395
|
+
try {
|
|
1396
|
+
currentRenderTask.cancel();
|
|
1397
|
+
} catch {
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
try {
|
|
1401
|
+
pdfDoc.destroy();
|
|
1402
|
+
} catch {
|
|
1403
|
+
}
|
|
1404
|
+
container.remove();
|
|
1283
1405
|
ctx.container.innerHTML = "";
|
|
1284
1406
|
};
|
|
1285
1407
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1286
|
-
|
|
1408
|
+
const instance = {
|
|
1287
1409
|
destroy: cleanup,
|
|
1288
1410
|
zoomIn: () => {
|
|
1289
|
-
|
|
1290
|
-
|
|
1411
|
+
zoomScale = Math.min(3.5, zoomScale + 0.2);
|
|
1412
|
+
renderPage(currentPage);
|
|
1291
1413
|
},
|
|
1292
1414
|
zoomOut: () => {
|
|
1293
|
-
|
|
1294
|
-
|
|
1415
|
+
zoomScale = Math.max(0.3, zoomScale - 0.2);
|
|
1416
|
+
renderPage(currentPage);
|
|
1295
1417
|
},
|
|
1296
|
-
getZoom: () =>
|
|
1418
|
+
getZoom: () => zoomScale,
|
|
1297
1419
|
setZoom: (level) => {
|
|
1298
|
-
|
|
1299
|
-
|
|
1420
|
+
zoomScale = Math.max(0.3, Math.min(3.5, level));
|
|
1421
|
+
renderPage(currentPage);
|
|
1300
1422
|
},
|
|
1301
1423
|
fitToPage: () => {
|
|
1302
|
-
|
|
1303
|
-
|
|
1424
|
+
zoomScale = 1;
|
|
1425
|
+
renderPage(currentPage);
|
|
1304
1426
|
},
|
|
1305
1427
|
rotateCW: () => {
|
|
1306
1428
|
rotation = (rotation + 90) % 360;
|
|
1307
|
-
|
|
1429
|
+
renderPage(currentPage);
|
|
1308
1430
|
},
|
|
1309
1431
|
rotateCCW: () => {
|
|
1310
1432
|
rotation = (rotation - 90 + 360) % 360;
|
|
1311
|
-
|
|
1433
|
+
renderPage(currentPage);
|
|
1312
1434
|
},
|
|
1313
1435
|
getRotation: () => rotation,
|
|
1436
|
+
getPageCount: () => totalPages,
|
|
1437
|
+
getCurrentPage: () => currentPage,
|
|
1314
1438
|
goToPage: (page) => {
|
|
1315
|
-
|
|
1316
|
-
iframe.src = `${url}#page=${page}`;
|
|
1439
|
+
renderPage(page);
|
|
1317
1440
|
},
|
|
1318
|
-
getCurrentPage: () => currentPage,
|
|
1319
1441
|
download: () => {
|
|
1442
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1443
|
+
const url = URL.createObjectURL(blob);
|
|
1320
1444
|
const a = document.createElement("a");
|
|
1321
1445
|
a.href = url;
|
|
1322
1446
|
a.download = ctx.metadata.name || "document.pdf";
|
|
1323
1447
|
a.click();
|
|
1448
|
+
URL.revokeObjectURL(url);
|
|
1324
1449
|
},
|
|
1325
1450
|
print: () => {
|
|
1326
|
-
|
|
1451
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1452
|
+
const url = URL.createObjectURL(blob);
|
|
1453
|
+
const hiddenIframe = document.createElement("iframe");
|
|
1454
|
+
hiddenIframe.style.position = "fixed";
|
|
1455
|
+
hiddenIframe.style.right = "0";
|
|
1456
|
+
hiddenIframe.style.bottom = "0";
|
|
1457
|
+
hiddenIframe.style.width = "0";
|
|
1458
|
+
hiddenIframe.style.height = "0";
|
|
1459
|
+
hiddenIframe.style.border = "0";
|
|
1460
|
+
document.body.appendChild(hiddenIframe);
|
|
1461
|
+
hiddenIframe.src = url;
|
|
1462
|
+
hiddenIframe.onload = () => {
|
|
1463
|
+
setTimeout(() => {
|
|
1464
|
+
hiddenIframe.contentWindow?.print();
|
|
1465
|
+
setTimeout(() => {
|
|
1466
|
+
hiddenIframe.remove();
|
|
1467
|
+
URL.revokeObjectURL(url);
|
|
1468
|
+
}, 1e3);
|
|
1469
|
+
}, 300);
|
|
1470
|
+
};
|
|
1327
1471
|
},
|
|
1328
1472
|
getThumbnails: async () => {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1473
|
+
const thumbnails = [];
|
|
1474
|
+
const count = Math.min(totalPages, 50);
|
|
1475
|
+
for (let i = 1; i <= count; i++) {
|
|
1476
|
+
thumbnails.push({
|
|
1477
|
+
index: i,
|
|
1478
|
+
label: `Page ${i}`,
|
|
1479
|
+
render: async (thumbCanvas) => {
|
|
1480
|
+
try {
|
|
1481
|
+
const p = await pdfDoc.getPage(i);
|
|
1482
|
+
const baseVp = p.getViewport({ scale: 1 });
|
|
1483
|
+
const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
|
|
1484
|
+
const thumbVp = p.getViewport({ scale: thumbScale });
|
|
1485
|
+
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1486
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
1487
|
+
if (tCtx) {
|
|
1488
|
+
await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
|
|
1489
|
+
}
|
|
1490
|
+
} catch (e) {
|
|
1491
|
+
console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
|
|
1341
1492
|
}
|
|
1342
1493
|
}
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
return thumbnails;
|
|
1345
1497
|
}
|
|
1346
1498
|
};
|
|
1499
|
+
return instance;
|
|
1347
1500
|
}
|
|
1348
1501
|
};
|
|
1349
1502
|
function pdfPlugin() {
|
|
@@ -1670,7 +1823,31 @@ var DocxPlugin = class {
|
|
|
1670
1823
|
return this.mimeTypes.includes(mime || "");
|
|
1671
1824
|
}
|
|
1672
1825
|
getToolbarActions(instance) {
|
|
1673
|
-
|
|
1826
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1827
|
+
const actions = [];
|
|
1828
|
+
if (totalPages > 1) {
|
|
1829
|
+
actions.push({
|
|
1830
|
+
id: "page-nav",
|
|
1831
|
+
icon: "",
|
|
1832
|
+
label: "Page Navigation",
|
|
1833
|
+
type: "page-nav",
|
|
1834
|
+
group: "navigation",
|
|
1835
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
1836
|
+
max: totalPages,
|
|
1837
|
+
execute: (action, page) => {
|
|
1838
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1839
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1840
|
+
if (action === "prev") {
|
|
1841
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1842
|
+
} else if (action === "next") {
|
|
1843
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1844
|
+
} else if (typeof page === "number") {
|
|
1845
|
+
instance.goToPage?.(page);
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
});
|
|
1849
|
+
}
|
|
1850
|
+
actions.push(
|
|
1674
1851
|
{
|
|
1675
1852
|
id: "zoom-out",
|
|
1676
1853
|
icon: "zoom-out",
|
|
@@ -1711,7 +1888,8 @@ var DocxPlugin = class {
|
|
|
1711
1888
|
group: "actions",
|
|
1712
1889
|
execute: () => instance.print?.()
|
|
1713
1890
|
}
|
|
1714
|
-
|
|
1891
|
+
);
|
|
1892
|
+
return actions;
|
|
1715
1893
|
}
|
|
1716
1894
|
async render(ctx) {
|
|
1717
1895
|
const wrapper = document.createElement("div");
|
|
@@ -1787,7 +1965,51 @@ var DocxPlugin = class {
|
|
|
1787
1965
|
}
|
|
1788
1966
|
}
|
|
1789
1967
|
}
|
|
1968
|
+
const sections = wrapper.querySelectorAll("section.docx");
|
|
1969
|
+
const cards = wrapper.querySelectorAll(".fp-docx-page-card");
|
|
1970
|
+
const pageElements = sections.length > 0 ? sections : cards;
|
|
1971
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
1972
|
+
let currentPage = 1;
|
|
1973
|
+
let indicator = null;
|
|
1974
|
+
if (totalPages > 1) {
|
|
1975
|
+
indicator = document.createElement("div");
|
|
1976
|
+
indicator.className = "fp-docx-page-indicator";
|
|
1977
|
+
indicator.style.position = "sticky";
|
|
1978
|
+
indicator.style.bottom = "16px";
|
|
1979
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1980
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1981
|
+
indicator.style.color = "#f8fafc";
|
|
1982
|
+
indicator.style.fontSize = "12px";
|
|
1983
|
+
indicator.style.fontWeight = "600";
|
|
1984
|
+
indicator.style.padding = "5px 14px";
|
|
1985
|
+
indicator.style.borderRadius = "20px";
|
|
1986
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1987
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1988
|
+
indicator.style.zIndex = "10";
|
|
1989
|
+
indicator.style.userSelect = "none";
|
|
1990
|
+
indicator.style.pointerEvents = "none";
|
|
1991
|
+
indicator.style.textAlign = "center";
|
|
1992
|
+
indicator.style.width = "fit-content";
|
|
1993
|
+
indicator.style.margin = "16px auto 0";
|
|
1994
|
+
ctx.container.appendChild(indicator);
|
|
1995
|
+
}
|
|
1996
|
+
const showPage = (pageNum) => {
|
|
1997
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1998
|
+
if (pageElements.length > 1) {
|
|
1999
|
+
pageElements.forEach((sec, idx) => {
|
|
2000
|
+
sec.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
if (indicator) {
|
|
2004
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2005
|
+
}
|
|
2006
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2007
|
+
};
|
|
2008
|
+
if (totalPages > 1) {
|
|
2009
|
+
showPage(1);
|
|
2010
|
+
}
|
|
1790
2011
|
const cleanup = () => {
|
|
2012
|
+
indicator?.remove();
|
|
1791
2013
|
for (const url of createdBlobUrls) {
|
|
1792
2014
|
URL.revokeObjectURL(url);
|
|
1793
2015
|
}
|
|
@@ -1798,6 +2020,11 @@ var DocxPlugin = class {
|
|
|
1798
2020
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1799
2021
|
return {
|
|
1800
2022
|
destroy: cleanup,
|
|
2023
|
+
getPageCount: () => totalPages,
|
|
2024
|
+
getCurrentPage: () => currentPage,
|
|
2025
|
+
goToPage: (page) => {
|
|
2026
|
+
showPage(page);
|
|
2027
|
+
},
|
|
1801
2028
|
zoomIn: () => {
|
|
1802
2029
|
scale += 0.1;
|
|
1803
2030
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -2062,7 +2289,31 @@ var ExcelPlugin = class {
|
|
|
2062
2289
|
return this.mimeTypes.includes(mime || "");
|
|
2063
2290
|
}
|
|
2064
2291
|
getToolbarActions(instance) {
|
|
2065
|
-
|
|
2292
|
+
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2293
|
+
const actions = [];
|
|
2294
|
+
if (totalSheets > 1) {
|
|
2295
|
+
actions.push({
|
|
2296
|
+
id: "page-nav",
|
|
2297
|
+
icon: "",
|
|
2298
|
+
label: "Sheet Navigation",
|
|
2299
|
+
type: "page-nav",
|
|
2300
|
+
group: "navigation",
|
|
2301
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2302
|
+
max: totalSheets,
|
|
2303
|
+
execute: (action, sheet) => {
|
|
2304
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2305
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2306
|
+
if (action === "prev") {
|
|
2307
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2308
|
+
} else if (action === "next") {
|
|
2309
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2310
|
+
} else if (typeof sheet === "number") {
|
|
2311
|
+
instance.goToPage?.(sheet);
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
});
|
|
2315
|
+
}
|
|
2316
|
+
actions.push(
|
|
2066
2317
|
{
|
|
2067
2318
|
id: "zoom-out",
|
|
2068
2319
|
icon: "zoom-out",
|
|
@@ -2083,16 +2334,6 @@ var ExcelPlugin = class {
|
|
|
2083
2334
|
instance.zoomIn?.();
|
|
2084
2335
|
}
|
|
2085
2336
|
},
|
|
2086
|
-
{
|
|
2087
|
-
id: "page-nav",
|
|
2088
|
-
icon: "page-nav",
|
|
2089
|
-
label: "Sheet Navigation",
|
|
2090
|
-
type: "page-nav",
|
|
2091
|
-
group: "navigation",
|
|
2092
|
-
execute: (sheet) => {
|
|
2093
|
-
if (typeof sheet === "number") instance.goToPage?.(sheet);
|
|
2094
|
-
}
|
|
2095
|
-
},
|
|
2096
2337
|
{
|
|
2097
2338
|
id: "download",
|
|
2098
2339
|
icon: "download",
|
|
@@ -2113,7 +2354,8 @@ var ExcelPlugin = class {
|
|
|
2113
2354
|
instance.print?.();
|
|
2114
2355
|
}
|
|
2115
2356
|
}
|
|
2116
|
-
|
|
2357
|
+
);
|
|
2358
|
+
return actions;
|
|
2117
2359
|
}
|
|
2118
2360
|
async render(ctx) {
|
|
2119
2361
|
const container = document.createElement("div");
|
|
@@ -2197,6 +2439,7 @@ var ExcelPlugin = class {
|
|
|
2197
2439
|
b.style.fontWeight = "normal";
|
|
2198
2440
|
}
|
|
2199
2441
|
});
|
|
2442
|
+
ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
|
|
2200
2443
|
};
|
|
2201
2444
|
if (sheetNames.length > 0) {
|
|
2202
2445
|
sheetNames.forEach((name, idx) => {
|
|
@@ -2446,7 +2689,31 @@ var CodePlugin = class {
|
|
|
2446
2689
|
return false;
|
|
2447
2690
|
}
|
|
2448
2691
|
getToolbarActions(instance) {
|
|
2449
|
-
|
|
2692
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2693
|
+
const actions = [];
|
|
2694
|
+
if (totalPages > 1) {
|
|
2695
|
+
actions.push({
|
|
2696
|
+
id: "page-nav",
|
|
2697
|
+
icon: "",
|
|
2698
|
+
label: "Page Navigation",
|
|
2699
|
+
type: "page-nav",
|
|
2700
|
+
group: "navigation",
|
|
2701
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2702
|
+
max: totalPages,
|
|
2703
|
+
execute: (action, page) => {
|
|
2704
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2705
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2706
|
+
if (action === "prev") {
|
|
2707
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2708
|
+
} else if (action === "next") {
|
|
2709
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2710
|
+
} else if (typeof page === "number") {
|
|
2711
|
+
instance.goToPage?.(page);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
actions.push(
|
|
2450
2717
|
{
|
|
2451
2718
|
id: "zoom-out",
|
|
2452
2719
|
icon: "zoom-out",
|
|
@@ -2497,11 +2764,16 @@ var CodePlugin = class {
|
|
|
2497
2764
|
instance.print?.();
|
|
2498
2765
|
}
|
|
2499
2766
|
}
|
|
2500
|
-
|
|
2767
|
+
);
|
|
2768
|
+
return actions;
|
|
2501
2769
|
}
|
|
2502
2770
|
async render(ctx) {
|
|
2503
2771
|
const decoder = new TextDecoder("utf-8");
|
|
2504
|
-
const
|
|
2772
|
+
const fullText = decoder.decode(ctx.buffer);
|
|
2773
|
+
const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
|
|
2774
|
+
const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
2775
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
2776
|
+
let currentPage = 1;
|
|
2505
2777
|
const container = document.createElement("div");
|
|
2506
2778
|
container.style.width = "100%";
|
|
2507
2779
|
container.style.height = "100%";
|
|
@@ -2520,25 +2792,66 @@ var CodePlugin = class {
|
|
|
2520
2792
|
pre.style.wordBreak = "break-all";
|
|
2521
2793
|
const code = document.createElement("code");
|
|
2522
2794
|
const ext = (ctx.metadata.extension || "").replace(".", "");
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2795
|
+
const renderCodePage = (text) => {
|
|
2796
|
+
try {
|
|
2797
|
+
if (ext && hljs.getLanguage(ext)) {
|
|
2798
|
+
code.innerHTML = hljs.highlight(text, { language: ext }).value;
|
|
2799
|
+
} else {
|
|
2800
|
+
code.innerHTML = hljs.highlightAuto(text).value;
|
|
2801
|
+
}
|
|
2802
|
+
} catch {
|
|
2803
|
+
code.textContent = text;
|
|
2528
2804
|
}
|
|
2529
|
-
}
|
|
2530
|
-
|
|
2531
|
-
}
|
|
2805
|
+
};
|
|
2806
|
+
renderCodePage(rawPages[0] || fullText);
|
|
2532
2807
|
pre.appendChild(code);
|
|
2533
2808
|
container.appendChild(pre);
|
|
2809
|
+
let indicator = null;
|
|
2810
|
+
if (totalPages > 1) {
|
|
2811
|
+
indicator = document.createElement("div");
|
|
2812
|
+
indicator.className = "fp-code-page-indicator";
|
|
2813
|
+
indicator.style.position = "sticky";
|
|
2814
|
+
indicator.style.bottom = "16px";
|
|
2815
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2816
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2817
|
+
indicator.style.color = "#f8fafc";
|
|
2818
|
+
indicator.style.fontSize = "12px";
|
|
2819
|
+
indicator.style.fontWeight = "600";
|
|
2820
|
+
indicator.style.padding = "5px 14px";
|
|
2821
|
+
indicator.style.borderRadius = "20px";
|
|
2822
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2823
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2824
|
+
indicator.style.zIndex = "10";
|
|
2825
|
+
indicator.style.userSelect = "none";
|
|
2826
|
+
indicator.style.pointerEvents = "none";
|
|
2827
|
+
indicator.style.textAlign = "center";
|
|
2828
|
+
indicator.style.width = "fit-content";
|
|
2829
|
+
indicator.style.margin = "16px auto 0";
|
|
2830
|
+
container.appendChild(indicator);
|
|
2831
|
+
}
|
|
2832
|
+
const showPage = (pageNum) => {
|
|
2833
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2834
|
+
renderCodePage(rawPages[currentPage - 1] || fullText);
|
|
2835
|
+
if (indicator) {
|
|
2836
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2837
|
+
}
|
|
2838
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2839
|
+
};
|
|
2840
|
+
if (totalPages > 1) {
|
|
2841
|
+
showPage(1);
|
|
2842
|
+
}
|
|
2534
2843
|
ctx.container.appendChild(container);
|
|
2535
2844
|
const cleanup = () => {
|
|
2845
|
+
indicator?.remove();
|
|
2536
2846
|
container.remove();
|
|
2537
2847
|
ctx.container.innerHTML = "";
|
|
2538
2848
|
};
|
|
2539
2849
|
ctx.signal.addEventListener("abort", cleanup);
|
|
2540
2850
|
return {
|
|
2541
2851
|
destroy: cleanup,
|
|
2852
|
+
getPageCount: () => totalPages,
|
|
2853
|
+
getCurrentPage: () => currentPage,
|
|
2854
|
+
goToPage: (page) => showPage(page),
|
|
2542
2855
|
zoomIn: () => {
|
|
2543
2856
|
fontSize = Math.min(32, fontSize + 2);
|
|
2544
2857
|
pre.style.fontSize = `${fontSize}px`;
|
|
@@ -2566,7 +2879,7 @@ var CodePlugin = class {
|
|
|
2566
2879
|
window.print();
|
|
2567
2880
|
},
|
|
2568
2881
|
copy: () => {
|
|
2569
|
-
navigator.clipboard?.writeText(
|
|
2882
|
+
navigator.clipboard?.writeText(fullText);
|
|
2570
2883
|
}
|
|
2571
2884
|
};
|
|
2572
2885
|
}
|
|
@@ -3388,7 +3701,31 @@ var RtfPlugin = class {
|
|
|
3388
3701
|
return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
|
|
3389
3702
|
}
|
|
3390
3703
|
getToolbarActions(instance) {
|
|
3391
|
-
|
|
3704
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3705
|
+
const actions = [];
|
|
3706
|
+
if (totalPages > 1) {
|
|
3707
|
+
actions.push({
|
|
3708
|
+
id: "page-nav",
|
|
3709
|
+
icon: "",
|
|
3710
|
+
label: "Page Navigation",
|
|
3711
|
+
type: "page-nav",
|
|
3712
|
+
group: "navigation",
|
|
3713
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3714
|
+
max: totalPages,
|
|
3715
|
+
execute: (action, page) => {
|
|
3716
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3717
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3718
|
+
if (action === "prev") {
|
|
3719
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3720
|
+
} else if (action === "next") {
|
|
3721
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3722
|
+
} else if (typeof page === "number") {
|
|
3723
|
+
instance.goToPage?.(page);
|
|
3724
|
+
}
|
|
3725
|
+
}
|
|
3726
|
+
});
|
|
3727
|
+
}
|
|
3728
|
+
actions.push(
|
|
3392
3729
|
{
|
|
3393
3730
|
id: "zoom-out",
|
|
3394
3731
|
icon: "zoom-out",
|
|
@@ -3429,7 +3766,8 @@ var RtfPlugin = class {
|
|
|
3429
3766
|
group: "actions",
|
|
3430
3767
|
execute: () => instance.print?.()
|
|
3431
3768
|
}
|
|
3432
|
-
|
|
3769
|
+
);
|
|
3770
|
+
return actions;
|
|
3433
3771
|
}
|
|
3434
3772
|
async render(ctx) {
|
|
3435
3773
|
const wrapper = document.createElement("div");
|
|
@@ -3448,28 +3786,82 @@ var RtfPlugin = class {
|
|
|
3448
3786
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
3449
3787
|
ctx.container.appendChild(wrapper);
|
|
3450
3788
|
let scale = 1;
|
|
3789
|
+
let pageElements = [];
|
|
3451
3790
|
try {
|
|
3452
3791
|
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3453
3792
|
RTFJS.loggingEnabled(false);
|
|
3454
3793
|
}
|
|
3455
3794
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3456
3795
|
const htmlElements = await doc.render();
|
|
3457
|
-
|
|
3796
|
+
pageElements = htmlElements;
|
|
3797
|
+
for (let i = 0; i < htmlElements.length; i++) {
|
|
3798
|
+
const el = htmlElements[i];
|
|
3799
|
+
el.style.display = i === 0 ? "block" : "none";
|
|
3458
3800
|
wrapper.appendChild(el);
|
|
3459
3801
|
}
|
|
3460
3802
|
} catch (err) {
|
|
3461
3803
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
3462
3804
|
const text = new TextDecoder("latin1").decode(ctx.buffer);
|
|
3463
3805
|
const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
|
|
3464
|
-
|
|
3806
|
+
const pre = document.createElement("pre");
|
|
3807
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
3808
|
+
pre.style.fontFamily = "serif";
|
|
3809
|
+
pre.style.color = "#333";
|
|
3810
|
+
pre.textContent = clean;
|
|
3811
|
+
wrapper.appendChild(pre);
|
|
3812
|
+
pageElements = [pre];
|
|
3813
|
+
}
|
|
3814
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
3815
|
+
let currentPage = 1;
|
|
3816
|
+
let indicator = null;
|
|
3817
|
+
if (totalPages > 1) {
|
|
3818
|
+
indicator = document.createElement("div");
|
|
3819
|
+
indicator.className = "fp-rtf-page-indicator";
|
|
3820
|
+
indicator.style.position = "sticky";
|
|
3821
|
+
indicator.style.bottom = "16px";
|
|
3822
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3823
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
3824
|
+
indicator.style.color = "#f8fafc";
|
|
3825
|
+
indicator.style.fontSize = "12px";
|
|
3826
|
+
indicator.style.fontWeight = "600";
|
|
3827
|
+
indicator.style.padding = "5px 14px";
|
|
3828
|
+
indicator.style.borderRadius = "20px";
|
|
3829
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3830
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3831
|
+
indicator.style.zIndex = "10";
|
|
3832
|
+
indicator.style.userSelect = "none";
|
|
3833
|
+
indicator.style.pointerEvents = "none";
|
|
3834
|
+
indicator.style.textAlign = "center";
|
|
3835
|
+
indicator.style.width = "fit-content";
|
|
3836
|
+
indicator.style.margin = "16px auto 0";
|
|
3837
|
+
ctx.container.appendChild(indicator);
|
|
3838
|
+
}
|
|
3839
|
+
const showPage = (pageNum) => {
|
|
3840
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3841
|
+
if (totalPages > 1) {
|
|
3842
|
+
pageElements.forEach((el, idx) => {
|
|
3843
|
+
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
3844
|
+
});
|
|
3845
|
+
}
|
|
3846
|
+
if (indicator) {
|
|
3847
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3848
|
+
}
|
|
3849
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3850
|
+
};
|
|
3851
|
+
if (totalPages > 1) {
|
|
3852
|
+
showPage(1);
|
|
3465
3853
|
}
|
|
3466
3854
|
const cleanup = () => {
|
|
3855
|
+
indicator?.remove();
|
|
3467
3856
|
wrapper.remove();
|
|
3468
3857
|
ctx.container.innerHTML = "";
|
|
3469
3858
|
};
|
|
3470
3859
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3471
3860
|
return {
|
|
3472
3861
|
destroy: cleanup,
|
|
3862
|
+
getPageCount: () => totalPages,
|
|
3863
|
+
getCurrentPage: () => currentPage,
|
|
3864
|
+
goToPage: (page) => showPage(page),
|
|
3473
3865
|
zoomIn: () => {
|
|
3474
3866
|
scale += 0.1;
|
|
3475
3867
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -3657,41 +4049,41 @@ var OpenDocumentPlugin = class {
|
|
|
3657
4049
|
}
|
|
3658
4050
|
getToolbarActions(instance) {
|
|
3659
4051
|
const isPresentation = instance.isPresentation;
|
|
4052
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3660
4053
|
const actions = [];
|
|
3661
|
-
if (isPresentation) {
|
|
3662
|
-
|
|
3663
|
-
{
|
|
4054
|
+
if (isPresentation || totalPages > 1) {
|
|
4055
|
+
if (isPresentation) {
|
|
4056
|
+
actions.push({
|
|
3664
4057
|
id: "thumbnails",
|
|
3665
4058
|
icon: "thumbnails",
|
|
3666
4059
|
label: "Slide Thumbnails",
|
|
3667
4060
|
type: "button",
|
|
3668
4061
|
group: "navigation",
|
|
3669
4062
|
execute: () => instance.toggleThumbnails?.()
|
|
3670
|
-
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
}
|
|
4063
|
+
});
|
|
4064
|
+
}
|
|
4065
|
+
actions.push({
|
|
4066
|
+
id: "page-nav",
|
|
4067
|
+
icon: "",
|
|
4068
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
4069
|
+
type: "page-nav",
|
|
4070
|
+
group: "navigation",
|
|
4071
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4072
|
+
max: totalPages,
|
|
4073
|
+
execute: (action, page) => {
|
|
4074
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4075
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4076
|
+
if (action === "prev") {
|
|
4077
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4078
|
+
} else if (action === "next") {
|
|
4079
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4080
|
+
} else if (typeof page === "number") {
|
|
4081
|
+
instance.goToPage?.(page);
|
|
4082
|
+
} else if (typeof action === "number") {
|
|
4083
|
+
instance.goToPage?.(action);
|
|
3692
4084
|
}
|
|
3693
4085
|
}
|
|
3694
|
-
);
|
|
4086
|
+
});
|
|
3695
4087
|
}
|
|
3696
4088
|
actions.push(
|
|
3697
4089
|
{
|
|
@@ -4074,7 +4466,31 @@ var DocPlugin = class {
|
|
|
4074
4466
|
return this.mimeTypes.includes(mime || "");
|
|
4075
4467
|
}
|
|
4076
4468
|
getToolbarActions(instance) {
|
|
4077
|
-
|
|
4469
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4470
|
+
const actions = [];
|
|
4471
|
+
if (totalPages > 1) {
|
|
4472
|
+
actions.push({
|
|
4473
|
+
id: "page-nav",
|
|
4474
|
+
icon: "",
|
|
4475
|
+
label: "Page Navigation",
|
|
4476
|
+
type: "page-nav",
|
|
4477
|
+
group: "navigation",
|
|
4478
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4479
|
+
max: totalPages,
|
|
4480
|
+
execute: (action, page) => {
|
|
4481
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4482
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4483
|
+
if (action === "prev") {
|
|
4484
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4485
|
+
} else if (action === "next") {
|
|
4486
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4487
|
+
} else if (typeof page === "number") {
|
|
4488
|
+
instance.goToPage?.(page);
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
});
|
|
4492
|
+
}
|
|
4493
|
+
actions.push(
|
|
4078
4494
|
{
|
|
4079
4495
|
id: "zoom-out",
|
|
4080
4496
|
icon: "zoom-out",
|
|
@@ -4123,7 +4539,8 @@ var DocPlugin = class {
|
|
|
4123
4539
|
group: "actions",
|
|
4124
4540
|
execute: () => instance.print?.()
|
|
4125
4541
|
}
|
|
4126
|
-
|
|
4542
|
+
);
|
|
4543
|
+
return actions;
|
|
4127
4544
|
}
|
|
4128
4545
|
async render(ctx) {
|
|
4129
4546
|
const container = document.createElement("div");
|
|
@@ -4150,6 +4567,7 @@ var DocPlugin = class {
|
|
|
4150
4567
|
ctx.container.appendChild(container);
|
|
4151
4568
|
let scale = 1;
|
|
4152
4569
|
let extractedRawText = "";
|
|
4570
|
+
let isFallback = false;
|
|
4153
4571
|
try {
|
|
4154
4572
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4155
4573
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
@@ -4163,26 +4581,89 @@ var DocPlugin = class {
|
|
|
4163
4581
|
const tableStream = cfbf.readStream(tableName);
|
|
4164
4582
|
const text = this.extractDocText(wordDocStream, tableStream);
|
|
4165
4583
|
extractedRawText = text;
|
|
4166
|
-
wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
|
|
4167
4584
|
} catch (err) {
|
|
4168
4585
|
console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
|
|
4169
4586
|
const fallback = this.heuristicTextExtraction(ctx.buffer);
|
|
4170
4587
|
extractedRawText = fallback;
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4588
|
+
isFallback = true;
|
|
4589
|
+
}
|
|
4590
|
+
const rawPages = this.splitIntoPages(extractedRawText);
|
|
4591
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
4592
|
+
let currentPage = 1;
|
|
4593
|
+
wrapper.innerHTML = "";
|
|
4594
|
+
const pageCards = [];
|
|
4595
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4596
|
+
const pageCard = document.createElement("div");
|
|
4597
|
+
pageCard.className = "fp-doc-page-card";
|
|
4598
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
4599
|
+
pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
|
|
4600
|
+
pageCard.style.borderRadius = "4px";
|
|
4601
|
+
pageCard.style.padding = "56px 48px";
|
|
4602
|
+
pageCard.style.minHeight = "100%";
|
|
4603
|
+
pageCard.style.display = i === 0 ? "block" : "none";
|
|
4604
|
+
if (isFallback && i === 0) {
|
|
4605
|
+
pageCard.innerHTML = `
|
|
4606
|
+
<div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
|
|
4607
|
+
<h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
|
|
4608
|
+
<span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
|
|
4609
|
+
</div>
|
|
4610
|
+
${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
|
|
4611
|
+
`;
|
|
4612
|
+
} else {
|
|
4613
|
+
pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
|
|
4614
|
+
}
|
|
4615
|
+
wrapper.appendChild(pageCard);
|
|
4616
|
+
pageCards.push(pageCard);
|
|
4617
|
+
}
|
|
4618
|
+
let indicator = null;
|
|
4619
|
+
if (totalPages > 1) {
|
|
4620
|
+
indicator = document.createElement("div");
|
|
4621
|
+
indicator.className = "fp-doc-page-indicator";
|
|
4622
|
+
indicator.style.position = "sticky";
|
|
4623
|
+
indicator.style.bottom = "16px";
|
|
4624
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4625
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4626
|
+
indicator.style.color = "#f8fafc";
|
|
4627
|
+
indicator.style.fontSize = "12px";
|
|
4628
|
+
indicator.style.fontWeight = "600";
|
|
4629
|
+
indicator.style.padding = "5px 14px";
|
|
4630
|
+
indicator.style.borderRadius = "20px";
|
|
4631
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4632
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4633
|
+
indicator.style.zIndex = "10";
|
|
4634
|
+
indicator.style.userSelect = "none";
|
|
4635
|
+
indicator.style.pointerEvents = "none";
|
|
4636
|
+
indicator.style.textAlign = "center";
|
|
4637
|
+
indicator.style.width = "fit-content";
|
|
4638
|
+
indicator.style.margin = "16px auto 0";
|
|
4639
|
+
container.appendChild(indicator);
|
|
4640
|
+
}
|
|
4641
|
+
const showPage = (pageNum) => {
|
|
4642
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4643
|
+
if (totalPages > 1) {
|
|
4644
|
+
pageCards.forEach((card, idx) => {
|
|
4645
|
+
card.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4646
|
+
});
|
|
4647
|
+
}
|
|
4648
|
+
if (indicator) {
|
|
4649
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4650
|
+
}
|
|
4651
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4652
|
+
};
|
|
4653
|
+
if (totalPages > 1) {
|
|
4654
|
+
showPage(1);
|
|
4178
4655
|
}
|
|
4179
4656
|
const cleanup = () => {
|
|
4657
|
+
indicator?.remove();
|
|
4180
4658
|
container.remove();
|
|
4181
4659
|
ctx.container.innerHTML = "";
|
|
4182
4660
|
};
|
|
4183
4661
|
ctx.signal.addEventListener("abort", cleanup);
|
|
4184
4662
|
return {
|
|
4185
4663
|
destroy: cleanup,
|
|
4664
|
+
getPageCount: () => totalPages,
|
|
4665
|
+
getCurrentPage: () => currentPage,
|
|
4666
|
+
goToPage: (page) => showPage(page),
|
|
4186
4667
|
zoomIn: () => {
|
|
4187
4668
|
scale += 0.1;
|
|
4188
4669
|
wrapper.style.transform = `scale(${scale})`;
|
|
@@ -4314,11 +4795,16 @@ var DocPlugin = class {
|
|
|
4314
4795
|
heuristicTextExtraction(buffer) {
|
|
4315
4796
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
4316
4797
|
}
|
|
4798
|
+
splitIntoPages(text) {
|
|
4799
|
+
if (!text) return [""];
|
|
4800
|
+
const parts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
4801
|
+
return parts.length > 0 ? parts : [text];
|
|
4802
|
+
}
|
|
4317
4803
|
/**
|
|
4318
4804
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
4319
4805
|
*/
|
|
4320
4806
|
formatDocToHtml(text, filename) {
|
|
4321
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").
|
|
4807
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
4322
4808
|
let html = "";
|
|
4323
4809
|
let inList = false;
|
|
4324
4810
|
for (const rawLine of lines) {
|