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