@files-preview-app/preview-file 1.2.4 → 1.2.6
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 +1043 -211
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +1042 -211
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +1043 -211
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1042 -211
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1043 -211
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +1042 -211
- package/dist/react.js.map +1 -1
- package/dist/styles.css +28 -1
- package/dist/vue.cjs +1043 -211
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +1042 -211
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import DOMPurify2 from 'dompurify';
|
|
2
|
+
import * as pdfjsLib from 'pdfjs-dist';
|
|
2
3
|
import * as docx from 'docx-preview';
|
|
3
4
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
4
5
|
import * as XLSX from 'xlsx';
|
|
@@ -522,11 +523,21 @@ var ToolbarController = class {
|
|
|
522
523
|
el;
|
|
523
524
|
toolbarEl;
|
|
524
525
|
actions = [];
|
|
526
|
+
pageInputEl = null;
|
|
527
|
+
pageLabelEl = null;
|
|
525
528
|
constructor(container) {
|
|
526
529
|
this.el = container;
|
|
527
530
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
528
531
|
this.el.appendChild(this.toolbarEl);
|
|
529
532
|
}
|
|
533
|
+
setPage(page, max) {
|
|
534
|
+
if (this.pageInputEl) {
|
|
535
|
+
this.pageInputEl.value = page.toString();
|
|
536
|
+
}
|
|
537
|
+
if (max !== void 0 && this.pageLabelEl) {
|
|
538
|
+
this.pageLabelEl.textContent = ` / ${max}`;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
530
541
|
update(actions) {
|
|
531
542
|
this.actions = actions;
|
|
532
543
|
this.render();
|
|
@@ -569,6 +580,7 @@ var ToolbarController = class {
|
|
|
569
580
|
if (action.type === "separator") {
|
|
570
581
|
groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
|
|
571
582
|
} else if (action.type === "page-nav") {
|
|
583
|
+
const max = action.max ?? 1;
|
|
572
584
|
const prevBtn = this.createButton(
|
|
573
585
|
"prev",
|
|
574
586
|
ICON_PAGE_PREV,
|
|
@@ -587,7 +599,6 @@ var ToolbarController = class {
|
|
|
587
599
|
"Next Page",
|
|
588
600
|
() => {
|
|
589
601
|
const cur = parseInt(input.value, 10) || 1;
|
|
590
|
-
const max = action.max ?? 1;
|
|
591
602
|
if (cur < max) {
|
|
592
603
|
input.value = (cur + 1).toString();
|
|
593
604
|
action.execute("next", cur + 1);
|
|
@@ -599,15 +610,18 @@ var ToolbarController = class {
|
|
|
599
610
|
type: "number",
|
|
600
611
|
value: (action.value ?? 1).toString(),
|
|
601
612
|
min: "1",
|
|
602
|
-
max:
|
|
613
|
+
max: max.toString()
|
|
603
614
|
});
|
|
604
615
|
input.addEventListener("change", () => {
|
|
605
|
-
|
|
606
|
-
if (
|
|
607
|
-
|
|
608
|
-
|
|
616
|
+
let val = parseInt(input.value, 10);
|
|
617
|
+
if (isNaN(val)) val = 1;
|
|
618
|
+
val = Math.max(1, Math.min(max, val));
|
|
619
|
+
input.value = val.toString();
|
|
620
|
+
action.execute("go", val);
|
|
609
621
|
});
|
|
610
|
-
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${
|
|
622
|
+
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
623
|
+
this.pageInputEl = input;
|
|
624
|
+
this.pageLabelEl = label;
|
|
611
625
|
groupEl.appendChild(prevBtn);
|
|
612
626
|
groupEl.appendChild(input);
|
|
613
627
|
groupEl.appendChild(label);
|
|
@@ -734,6 +748,8 @@ var FilePreviewViewer = class {
|
|
|
734
748
|
keyHandler = null;
|
|
735
749
|
currentContainer = null;
|
|
736
750
|
currentOptions = {};
|
|
751
|
+
resizeObserver = null;
|
|
752
|
+
fullscreenHandler = null;
|
|
737
753
|
/**
|
|
738
754
|
* Register a preview plugin.
|
|
739
755
|
*/
|
|
@@ -785,7 +801,13 @@ var FilePreviewViewer = class {
|
|
|
785
801
|
buffer,
|
|
786
802
|
options,
|
|
787
803
|
signal,
|
|
788
|
-
emit: (event, payload) =>
|
|
804
|
+
emit: (event, payload) => {
|
|
805
|
+
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
806
|
+
const total = payload.total ?? payload.totalPages;
|
|
807
|
+
this.toolbar?.setPage(payload.page, total);
|
|
808
|
+
}
|
|
809
|
+
this.eventEmitter.emit(event, payload);
|
|
810
|
+
}
|
|
789
811
|
});
|
|
790
812
|
this.activeInstance = instance;
|
|
791
813
|
this.hideLoading();
|
|
@@ -800,12 +822,31 @@ var FilePreviewViewer = class {
|
|
|
800
822
|
label: "Toggle Fullscreen",
|
|
801
823
|
type: "button",
|
|
802
824
|
group: "view",
|
|
803
|
-
execute: () => {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
825
|
+
execute: async () => {
|
|
826
|
+
try {
|
|
827
|
+
const isNativeFs = !!document.fullscreenElement;
|
|
828
|
+
const isCssFs = this.wrapperEl?.classList.contains("fp-fullscreen-active");
|
|
829
|
+
if (!isNativeFs && !isCssFs) {
|
|
830
|
+
if (this.wrapperEl?.requestFullscreen) {
|
|
831
|
+
await this.wrapperEl.requestFullscreen().catch(() => {
|
|
832
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
833
|
+
});
|
|
834
|
+
} else {
|
|
835
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
836
|
+
}
|
|
837
|
+
} else {
|
|
838
|
+
if (document.fullscreenElement) {
|
|
839
|
+
await document.exitFullscreen().catch(() => {
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
843
|
+
}
|
|
844
|
+
} catch {
|
|
845
|
+
this.wrapperEl?.classList.toggle("fp-fullscreen-active");
|
|
808
846
|
}
|
|
847
|
+
setTimeout(() => {
|
|
848
|
+
this.activeInstance?.fitToPage?.();
|
|
849
|
+
}, 120);
|
|
809
850
|
}
|
|
810
851
|
});
|
|
811
852
|
}
|
|
@@ -819,6 +860,7 @@ var FilePreviewViewer = class {
|
|
|
819
860
|
if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
|
|
820
861
|
this.thumbnailPanel.update(thumbnails, (index) => {
|
|
821
862
|
instance.goToPage?.(index + 1);
|
|
863
|
+
this.toolbar?.setPage(index + 1);
|
|
822
864
|
});
|
|
823
865
|
if (options.showThumbnails) {
|
|
824
866
|
this.thumbnailPanel.show();
|
|
@@ -854,6 +896,15 @@ var FilePreviewViewer = class {
|
|
|
854
896
|
window.removeEventListener("keydown", this.keyHandler);
|
|
855
897
|
this.keyHandler = null;
|
|
856
898
|
}
|
|
899
|
+
if (this.resizeObserver) {
|
|
900
|
+
this.resizeObserver.disconnect();
|
|
901
|
+
this.resizeObserver = null;
|
|
902
|
+
}
|
|
903
|
+
if (this.fullscreenHandler) {
|
|
904
|
+
document.removeEventListener("fullscreenchange", this.fullscreenHandler);
|
|
905
|
+
document.removeEventListener("webkitfullscreenchange", this.fullscreenHandler);
|
|
906
|
+
this.fullscreenHandler = null;
|
|
907
|
+
}
|
|
857
908
|
this.abort();
|
|
858
909
|
this.destroyInstance();
|
|
859
910
|
this.toolbar?.destroy();
|
|
@@ -933,6 +984,25 @@ var FilePreviewViewer = class {
|
|
|
933
984
|
this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
|
|
934
985
|
this.setupKeyboardShortcuts();
|
|
935
986
|
this.setupDragAndDrop(container, options);
|
|
987
|
+
this.setupResizeAndFullscreenListeners();
|
|
988
|
+
}
|
|
989
|
+
setupResizeAndFullscreenListeners() {
|
|
990
|
+
if (this.fullscreenHandler) return;
|
|
991
|
+
this.fullscreenHandler = () => {
|
|
992
|
+
setTimeout(() => {
|
|
993
|
+
this.activeInstance?.fitToPage?.();
|
|
994
|
+
}, 100);
|
|
995
|
+
};
|
|
996
|
+
document.addEventListener("fullscreenchange", this.fullscreenHandler);
|
|
997
|
+
document.addEventListener("webkitfullscreenchange", this.fullscreenHandler);
|
|
998
|
+
if (typeof ResizeObserver !== "undefined" && this.contentEl) {
|
|
999
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
1000
|
+
if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
|
|
1001
|
+
this.activeInstance.fitToPage?.();
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
this.resizeObserver.observe(this.contentEl);
|
|
1005
|
+
}
|
|
936
1006
|
}
|
|
937
1007
|
setupKeyboardShortcuts() {
|
|
938
1008
|
if (this.keyHandler) return;
|
|
@@ -944,9 +1014,13 @@ var FilePreviewViewer = class {
|
|
|
944
1014
|
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
945
1015
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
946
1016
|
this.activeInstance.goToPage?.(cur + 1);
|
|
1017
|
+
const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
|
|
1018
|
+
this.toolbar?.setPage(nextCur);
|
|
947
1019
|
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
948
1020
|
const cur = this.activeInstance.getCurrentPage?.() ?? 1;
|
|
949
1021
|
this.activeInstance.goToPage?.(Math.max(1, cur - 1));
|
|
1022
|
+
const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
|
|
1023
|
+
this.toolbar?.setPage(prevCur);
|
|
950
1024
|
} else if (e.key === "+" || e.key === "=") {
|
|
951
1025
|
this.activeInstance.zoomIn?.();
|
|
952
1026
|
} else if (e.key === "-" || e.key === "_") {
|
|
@@ -1212,30 +1286,62 @@ var CfbfReader = class {
|
|
|
1212
1286
|
return result.subarray(0, targetSize);
|
|
1213
1287
|
}
|
|
1214
1288
|
};
|
|
1215
|
-
|
|
1216
|
-
|
|
1289
|
+
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1290
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1291
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1217
1294
|
var PdfPlugin = class {
|
|
1218
1295
|
id = "pdf";
|
|
1219
|
-
name = "PDF Preview";
|
|
1296
|
+
name = "PDF Document Preview";
|
|
1220
1297
|
extensions = [".pdf"];
|
|
1221
1298
|
mimeTypes = ["application/pdf"];
|
|
1222
1299
|
weight = 100;
|
|
1223
1300
|
supports(file) {
|
|
1224
1301
|
const ext = file.metadata.extension?.toLowerCase();
|
|
1225
1302
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
1226
|
-
|
|
1303
|
+
if (ext) return this.extensions.includes(ext);
|
|
1304
|
+
return this.mimeTypes.includes(mime || "");
|
|
1227
1305
|
}
|
|
1228
1306
|
getToolbarActions(instance) {
|
|
1307
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1308
|
+
const curPage = instance.getCurrentPage?.() ?? 1;
|
|
1229
1309
|
return [
|
|
1310
|
+
{
|
|
1311
|
+
id: "thumbnails",
|
|
1312
|
+
icon: "thumbnails",
|
|
1313
|
+
label: "Page Thumbnails",
|
|
1314
|
+
type: "button",
|
|
1315
|
+
group: "navigation",
|
|
1316
|
+
execute: () => instance.toggleThumbnails?.()
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
id: "page-nav",
|
|
1320
|
+
icon: "",
|
|
1321
|
+
label: "Page Navigation",
|
|
1322
|
+
type: "page-nav",
|
|
1323
|
+
group: "navigation",
|
|
1324
|
+
value: curPage,
|
|
1325
|
+
max: totalPages,
|
|
1326
|
+
execute: (action, page) => {
|
|
1327
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1328
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1329
|
+
if (action === "prev") {
|
|
1330
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1331
|
+
} else if (action === "next") {
|
|
1332
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1333
|
+
} else if (typeof page === "number") {
|
|
1334
|
+
instance.goToPage?.(page);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
},
|
|
1230
1338
|
{
|
|
1231
1339
|
id: "zoom-out",
|
|
1232
1340
|
icon: "zoom-out",
|
|
1233
1341
|
label: "Zoom Out",
|
|
1234
1342
|
type: "button",
|
|
1235
1343
|
group: "zoom",
|
|
1236
|
-
execute: () =>
|
|
1237
|
-
instance.zoomOut?.();
|
|
1238
|
-
}
|
|
1344
|
+
execute: () => instance.zoomOut?.()
|
|
1239
1345
|
},
|
|
1240
1346
|
{
|
|
1241
1347
|
id: "zoom-in",
|
|
@@ -1243,9 +1349,7 @@ var PdfPlugin = class {
|
|
|
1243
1349
|
label: "Zoom In",
|
|
1244
1350
|
type: "button",
|
|
1245
1351
|
group: "zoom",
|
|
1246
|
-
execute: () =>
|
|
1247
|
-
instance.zoomIn?.();
|
|
1248
|
-
}
|
|
1352
|
+
execute: () => instance.zoomIn?.()
|
|
1249
1353
|
},
|
|
1250
1354
|
{
|
|
1251
1355
|
id: "fit-page",
|
|
@@ -1253,39 +1357,23 @@ var PdfPlugin = class {
|
|
|
1253
1357
|
label: "Fit to Page",
|
|
1254
1358
|
type: "button",
|
|
1255
1359
|
group: "zoom",
|
|
1256
|
-
execute: () =>
|
|
1257
|
-
instance.fitToPage?.();
|
|
1258
|
-
}
|
|
1360
|
+
execute: () => instance.fitToPage?.()
|
|
1259
1361
|
},
|
|
1260
1362
|
{
|
|
1261
1363
|
id: "rotate-cw",
|
|
1262
1364
|
icon: "rotate-cw",
|
|
1263
|
-
label: "Rotate",
|
|
1365
|
+
label: "Rotate Clockwise",
|
|
1264
1366
|
type: "button",
|
|
1265
1367
|
group: "view",
|
|
1266
|
-
execute: () =>
|
|
1267
|
-
instance.rotateCW?.();
|
|
1268
|
-
}
|
|
1269
|
-
},
|
|
1270
|
-
{
|
|
1271
|
-
id: "page-nav",
|
|
1272
|
-
icon: "page-nav",
|
|
1273
|
-
label: "Page Navigation",
|
|
1274
|
-
type: "page-nav",
|
|
1275
|
-
group: "navigation",
|
|
1276
|
-
execute: (page) => {
|
|
1277
|
-
if (typeof page === "number") instance.goToPage?.(page);
|
|
1278
|
-
}
|
|
1368
|
+
execute: () => instance.rotateCW?.()
|
|
1279
1369
|
},
|
|
1280
1370
|
{
|
|
1281
1371
|
id: "download",
|
|
1282
1372
|
icon: "download",
|
|
1283
|
-
label: "Download",
|
|
1373
|
+
label: "Download PDF",
|
|
1284
1374
|
type: "button",
|
|
1285
1375
|
group: "actions",
|
|
1286
|
-
execute: () =>
|
|
1287
|
-
instance.download?.();
|
|
1288
|
-
}
|
|
1376
|
+
execute: () => instance.download?.()
|
|
1289
1377
|
},
|
|
1290
1378
|
{
|
|
1291
1379
|
id: "print",
|
|
@@ -1293,99 +1381,224 @@ var PdfPlugin = class {
|
|
|
1293
1381
|
label: "Print",
|
|
1294
1382
|
type: "button",
|
|
1295
1383
|
group: "actions",
|
|
1296
|
-
execute: () =>
|
|
1297
|
-
instance.print?.();
|
|
1298
|
-
}
|
|
1384
|
+
execute: () => instance.print?.()
|
|
1299
1385
|
}
|
|
1300
1386
|
];
|
|
1301
1387
|
}
|
|
1302
1388
|
async render(ctx) {
|
|
1303
|
-
const
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1389
|
+
const container = document.createElement("div");
|
|
1390
|
+
container.className = "fp-pdf-container";
|
|
1391
|
+
container.style.width = "100%";
|
|
1392
|
+
container.style.height = "100%";
|
|
1393
|
+
container.style.overflow = "auto";
|
|
1394
|
+
container.style.display = "flex";
|
|
1395
|
+
container.style.flexDirection = "column";
|
|
1396
|
+
container.style.alignItems = "center";
|
|
1397
|
+
container.style.justifyContent = "flex-start";
|
|
1398
|
+
container.style.padding = "20px 16px";
|
|
1399
|
+
container.style.backgroundColor = "#0f172a";
|
|
1400
|
+
container.style.boxSizing = "border-box";
|
|
1401
|
+
container.style.position = "relative";
|
|
1402
|
+
const pageCard = document.createElement("div");
|
|
1403
|
+
pageCard.className = "fp-pdf-page-card";
|
|
1404
|
+
pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
|
|
1405
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
1406
|
+
pageCard.style.borderRadius = "4px";
|
|
1407
|
+
pageCard.style.overflow = "hidden";
|
|
1408
|
+
pageCard.style.lineHeight = "0";
|
|
1409
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
1410
|
+
pageCard.style.position = "relative";
|
|
1411
|
+
pageCard.style.flexShrink = "0";
|
|
1412
|
+
let canvas = document.createElement("canvas");
|
|
1413
|
+
pageCard.appendChild(canvas);
|
|
1414
|
+
container.appendChild(pageCard);
|
|
1415
|
+
const indicator = document.createElement("div");
|
|
1416
|
+
indicator.className = "fp-pdf-page-indicator";
|
|
1417
|
+
indicator.style.position = "sticky";
|
|
1418
|
+
indicator.style.bottom = "16px";
|
|
1419
|
+
indicator.style.marginTop = "16px";
|
|
1420
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1421
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
1422
|
+
indicator.style.color = "#f8fafc";
|
|
1423
|
+
indicator.style.fontSize = "12px";
|
|
1424
|
+
indicator.style.fontWeight = "600";
|
|
1425
|
+
indicator.style.padding = "5px 14px";
|
|
1426
|
+
indicator.style.borderRadius = "20px";
|
|
1427
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1428
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1429
|
+
indicator.style.zIndex = "10";
|
|
1430
|
+
indicator.style.userSelect = "none";
|
|
1431
|
+
indicator.style.pointerEvents = "none";
|
|
1432
|
+
container.appendChild(indicator);
|
|
1433
|
+
ctx.container.appendChild(container);
|
|
1434
|
+
const loadingTask = pdfjsLib.getDocument({
|
|
1435
|
+
data: new Uint8Array(ctx.buffer),
|
|
1436
|
+
cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
|
|
1437
|
+
cMapPacked: true,
|
|
1438
|
+
standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
|
|
1439
|
+
});
|
|
1440
|
+
const pdfDoc = await loadingTask.promise;
|
|
1441
|
+
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1319
1442
|
let currentPage = 1;
|
|
1320
|
-
let
|
|
1443
|
+
let zoomScale = 1;
|
|
1321
1444
|
let rotation = 0;
|
|
1445
|
+
let currentRenderTask = null;
|
|
1446
|
+
const renderPage = async (pageNum) => {
|
|
1447
|
+
if (currentRenderTask) {
|
|
1448
|
+
try {
|
|
1449
|
+
currentRenderTask.cancel();
|
|
1450
|
+
} catch {
|
|
1451
|
+
}
|
|
1452
|
+
currentRenderTask = null;
|
|
1453
|
+
}
|
|
1454
|
+
const newCanvas = document.createElement("canvas");
|
|
1455
|
+
pageCard.replaceChild(newCanvas, canvas);
|
|
1456
|
+
canvas = newCanvas;
|
|
1457
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1458
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1459
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1460
|
+
const page = await pdfDoc.getPage(currentPage);
|
|
1461
|
+
const containerWidth = container.clientWidth || 900;
|
|
1462
|
+
const containerHeight = container.clientHeight || 700;
|
|
1463
|
+
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1464
|
+
const availWidth = Math.max(100, containerWidth - 48);
|
|
1465
|
+
const availHeight = Math.max(100, containerHeight - 88);
|
|
1466
|
+
const scaleW = availWidth / unscaledVp.width;
|
|
1467
|
+
const scaleH = availHeight / unscaledVp.height;
|
|
1468
|
+
const fitScale = Math.min(scaleW, scaleH);
|
|
1469
|
+
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1470
|
+
const pixelRatio = window.devicePixelRatio || 1;
|
|
1471
|
+
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
1472
|
+
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1473
|
+
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1474
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
1475
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
1476
|
+
const canvasCtx = canvas.getContext("2d");
|
|
1477
|
+
if (!canvasCtx) return;
|
|
1478
|
+
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
1479
|
+
currentRenderTask = page.render({
|
|
1480
|
+
canvasContext: canvasCtx,
|
|
1481
|
+
viewport
|
|
1482
|
+
});
|
|
1483
|
+
try {
|
|
1484
|
+
await currentRenderTask.promise;
|
|
1485
|
+
} catch (err) {
|
|
1486
|
+
if (err?.name !== "RenderingCancelledException") {
|
|
1487
|
+
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1488
|
+
}
|
|
1489
|
+
} finally {
|
|
1490
|
+
currentRenderTask = null;
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
await renderPage(1);
|
|
1322
1494
|
const cleanup = () => {
|
|
1323
|
-
|
|
1324
|
-
|
|
1495
|
+
if (currentRenderTask) {
|
|
1496
|
+
try {
|
|
1497
|
+
currentRenderTask.cancel();
|
|
1498
|
+
} catch {
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
try {
|
|
1502
|
+
pdfDoc.destroy();
|
|
1503
|
+
} catch {
|
|
1504
|
+
}
|
|
1505
|
+
container.remove();
|
|
1325
1506
|
ctx.container.innerHTML = "";
|
|
1326
1507
|
};
|
|
1327
1508
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1328
|
-
|
|
1509
|
+
const instance = {
|
|
1329
1510
|
destroy: cleanup,
|
|
1330
1511
|
zoomIn: () => {
|
|
1331
|
-
|
|
1332
|
-
|
|
1512
|
+
zoomScale = Math.min(3.5, zoomScale + 0.2);
|
|
1513
|
+
renderPage(currentPage);
|
|
1333
1514
|
},
|
|
1334
1515
|
zoomOut: () => {
|
|
1335
|
-
|
|
1336
|
-
|
|
1516
|
+
zoomScale = Math.max(0.3, zoomScale - 0.2);
|
|
1517
|
+
renderPage(currentPage);
|
|
1337
1518
|
},
|
|
1338
|
-
getZoom: () =>
|
|
1519
|
+
getZoom: () => zoomScale,
|
|
1339
1520
|
setZoom: (level) => {
|
|
1340
|
-
|
|
1341
|
-
|
|
1521
|
+
zoomScale = Math.max(0.3, Math.min(3.5, level));
|
|
1522
|
+
renderPage(currentPage);
|
|
1342
1523
|
},
|
|
1343
1524
|
fitToPage: () => {
|
|
1344
|
-
|
|
1345
|
-
|
|
1525
|
+
zoomScale = 1;
|
|
1526
|
+
rotation = 0;
|
|
1527
|
+
renderPage(currentPage);
|
|
1346
1528
|
},
|
|
1347
1529
|
rotateCW: () => {
|
|
1348
1530
|
rotation = (rotation + 90) % 360;
|
|
1349
|
-
|
|
1531
|
+
renderPage(currentPage);
|
|
1350
1532
|
},
|
|
1351
1533
|
rotateCCW: () => {
|
|
1352
1534
|
rotation = (rotation - 90 + 360) % 360;
|
|
1353
|
-
|
|
1535
|
+
renderPage(currentPage);
|
|
1354
1536
|
},
|
|
1355
1537
|
getRotation: () => rotation,
|
|
1538
|
+
getPageCount: () => totalPages,
|
|
1539
|
+
getCurrentPage: () => currentPage,
|
|
1356
1540
|
goToPage: (page) => {
|
|
1357
|
-
|
|
1358
|
-
iframe.src = `${url}#page=${page}`;
|
|
1541
|
+
renderPage(page);
|
|
1359
1542
|
},
|
|
1360
|
-
getCurrentPage: () => currentPage,
|
|
1361
1543
|
download: () => {
|
|
1544
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1545
|
+
const url = URL.createObjectURL(blob);
|
|
1362
1546
|
const a = document.createElement("a");
|
|
1363
1547
|
a.href = url;
|
|
1364
1548
|
a.download = ctx.metadata.name || "document.pdf";
|
|
1365
1549
|
a.click();
|
|
1550
|
+
URL.revokeObjectURL(url);
|
|
1366
1551
|
},
|
|
1367
1552
|
print: () => {
|
|
1368
|
-
|
|
1553
|
+
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1554
|
+
const url = URL.createObjectURL(blob);
|
|
1555
|
+
const hiddenIframe = document.createElement("iframe");
|
|
1556
|
+
hiddenIframe.style.position = "fixed";
|
|
1557
|
+
hiddenIframe.style.right = "0";
|
|
1558
|
+
hiddenIframe.style.bottom = "0";
|
|
1559
|
+
hiddenIframe.style.width = "0";
|
|
1560
|
+
hiddenIframe.style.height = "0";
|
|
1561
|
+
hiddenIframe.style.border = "0";
|
|
1562
|
+
document.body.appendChild(hiddenIframe);
|
|
1563
|
+
hiddenIframe.src = url;
|
|
1564
|
+
hiddenIframe.onload = () => {
|
|
1565
|
+
setTimeout(() => {
|
|
1566
|
+
hiddenIframe.contentWindow?.print();
|
|
1567
|
+
setTimeout(() => {
|
|
1568
|
+
hiddenIframe.remove();
|
|
1569
|
+
URL.revokeObjectURL(url);
|
|
1570
|
+
}, 1e3);
|
|
1571
|
+
}, 300);
|
|
1572
|
+
};
|
|
1369
1573
|
},
|
|
1370
1574
|
getThumbnails: async () => {
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1575
|
+
const thumbnails = [];
|
|
1576
|
+
const count = Math.min(totalPages, 50);
|
|
1577
|
+
for (let i = 1; i <= count; i++) {
|
|
1578
|
+
thumbnails.push({
|
|
1579
|
+
index: i,
|
|
1580
|
+
label: `Page ${i}`,
|
|
1581
|
+
render: async (thumbCanvas) => {
|
|
1582
|
+
try {
|
|
1583
|
+
const p = await pdfDoc.getPage(i);
|
|
1584
|
+
const baseVp = p.getViewport({ scale: 1 });
|
|
1585
|
+
const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
|
|
1586
|
+
const thumbVp = p.getViewport({ scale: thumbScale });
|
|
1587
|
+
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1588
|
+
const tCtx = thumbCanvas.getContext("2d");
|
|
1589
|
+
if (tCtx) {
|
|
1590
|
+
await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
|
|
1591
|
+
}
|
|
1592
|
+
} catch (e) {
|
|
1593
|
+
console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
|
|
1383
1594
|
}
|
|
1384
1595
|
}
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1596
|
+
});
|
|
1597
|
+
}
|
|
1598
|
+
return thumbnails;
|
|
1387
1599
|
}
|
|
1388
1600
|
};
|
|
1601
|
+
return instance;
|
|
1389
1602
|
}
|
|
1390
1603
|
};
|
|
1391
1604
|
function pdfPlugin() {
|
|
@@ -1712,7 +1925,31 @@ var DocxPlugin = class {
|
|
|
1712
1925
|
return this.mimeTypes.includes(mime || "");
|
|
1713
1926
|
}
|
|
1714
1927
|
getToolbarActions(instance) {
|
|
1715
|
-
|
|
1928
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
1929
|
+
const actions = [];
|
|
1930
|
+
if (totalPages > 1) {
|
|
1931
|
+
actions.push({
|
|
1932
|
+
id: "page-nav",
|
|
1933
|
+
icon: "",
|
|
1934
|
+
label: "Page Navigation",
|
|
1935
|
+
type: "page-nav",
|
|
1936
|
+
group: "navigation",
|
|
1937
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
1938
|
+
max: totalPages,
|
|
1939
|
+
execute: (action, page) => {
|
|
1940
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
1941
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
1942
|
+
if (action === "prev") {
|
|
1943
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
1944
|
+
} else if (action === "next") {
|
|
1945
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
1946
|
+
} else if (typeof page === "number") {
|
|
1947
|
+
instance.goToPage?.(page);
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
actions.push(
|
|
1716
1953
|
{
|
|
1717
1954
|
id: "zoom-out",
|
|
1718
1955
|
icon: "zoom-out",
|
|
@@ -1737,6 +1974,14 @@ var DocxPlugin = class {
|
|
|
1737
1974
|
group: "zoom",
|
|
1738
1975
|
execute: () => instance.fitToPage?.()
|
|
1739
1976
|
},
|
|
1977
|
+
{
|
|
1978
|
+
id: "rotate-cw",
|
|
1979
|
+
icon: "rotate-cw",
|
|
1980
|
+
label: "Rotate",
|
|
1981
|
+
type: "button",
|
|
1982
|
+
group: "view",
|
|
1983
|
+
execute: () => instance.rotateCW?.()
|
|
1984
|
+
},
|
|
1740
1985
|
{
|
|
1741
1986
|
id: "download",
|
|
1742
1987
|
icon: "download",
|
|
@@ -1753,7 +1998,8 @@ var DocxPlugin = class {
|
|
|
1753
1998
|
group: "actions",
|
|
1754
1999
|
execute: () => instance.print?.()
|
|
1755
2000
|
}
|
|
1756
|
-
|
|
2001
|
+
);
|
|
2002
|
+
return actions;
|
|
1757
2003
|
}
|
|
1758
2004
|
async render(ctx) {
|
|
1759
2005
|
const wrapper = document.createElement("div");
|
|
@@ -1829,7 +2075,71 @@ var DocxPlugin = class {
|
|
|
1829
2075
|
}
|
|
1830
2076
|
}
|
|
1831
2077
|
}
|
|
2078
|
+
const sections = wrapper.querySelectorAll("section.docx");
|
|
2079
|
+
const cards = wrapper.querySelectorAll(".fp-docx-page-card");
|
|
2080
|
+
const pageElements = sections.length > 0 ? sections : cards;
|
|
2081
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
2082
|
+
let currentPage = 1;
|
|
2083
|
+
let indicator = null;
|
|
2084
|
+
if (totalPages > 1) {
|
|
2085
|
+
indicator = document.createElement("div");
|
|
2086
|
+
indicator.className = "fp-docx-page-indicator";
|
|
2087
|
+
indicator.style.position = "sticky";
|
|
2088
|
+
indicator.style.bottom = "16px";
|
|
2089
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2090
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
2091
|
+
indicator.style.color = "#f8fafc";
|
|
2092
|
+
indicator.style.fontSize = "12px";
|
|
2093
|
+
indicator.style.fontWeight = "600";
|
|
2094
|
+
indicator.style.padding = "5px 14px";
|
|
2095
|
+
indicator.style.borderRadius = "20px";
|
|
2096
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2097
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2098
|
+
indicator.style.zIndex = "10";
|
|
2099
|
+
indicator.style.userSelect = "none";
|
|
2100
|
+
indicator.style.pointerEvents = "none";
|
|
2101
|
+
indicator.style.textAlign = "center";
|
|
2102
|
+
indicator.style.width = "fit-content";
|
|
2103
|
+
indicator.style.margin = "16px auto 0";
|
|
2104
|
+
ctx.container.appendChild(indicator);
|
|
2105
|
+
}
|
|
2106
|
+
const showPage = (pageNum) => {
|
|
2107
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2108
|
+
if (pageElements.length > 1) {
|
|
2109
|
+
pageElements.forEach((sec, idx) => {
|
|
2110
|
+
sec.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
if (indicator) {
|
|
2114
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2115
|
+
}
|
|
2116
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2117
|
+
};
|
|
2118
|
+
if (totalPages > 1) {
|
|
2119
|
+
showPage(1);
|
|
2120
|
+
}
|
|
2121
|
+
scale = 1;
|
|
2122
|
+
let rotation = 0;
|
|
2123
|
+
const calculateFitScale = () => {
|
|
2124
|
+
const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
|
|
2125
|
+
const elW = activeEl.offsetWidth || 816;
|
|
2126
|
+
const elH = activeEl.offsetHeight || 1056;
|
|
2127
|
+
const availW = Math.max(200, ctx.container.clientWidth - 48);
|
|
2128
|
+
const availH = Math.max(200, ctx.container.clientHeight - 80);
|
|
2129
|
+
const sW = availW / elW;
|
|
2130
|
+
const sH = availH / elH;
|
|
2131
|
+
return Math.min(1.1, Math.min(sW, sH));
|
|
2132
|
+
};
|
|
2133
|
+
const applyTransform = () => {
|
|
2134
|
+
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
2135
|
+
wrapper.style.transformOrigin = "top center";
|
|
2136
|
+
};
|
|
2137
|
+
setTimeout(() => {
|
|
2138
|
+
scale = calculateFitScale();
|
|
2139
|
+
applyTransform();
|
|
2140
|
+
}, 60);
|
|
1832
2141
|
const cleanup = () => {
|
|
2142
|
+
indicator?.remove();
|
|
1833
2143
|
for (const url of createdBlobUrls) {
|
|
1834
2144
|
URL.revokeObjectURL(url);
|
|
1835
2145
|
}
|
|
@@ -1840,22 +2150,36 @@ var DocxPlugin = class {
|
|
|
1840
2150
|
ctx.signal.addEventListener("abort", cleanup);
|
|
1841
2151
|
return {
|
|
1842
2152
|
destroy: cleanup,
|
|
2153
|
+
getPageCount: () => totalPages,
|
|
2154
|
+
getCurrentPage: () => currentPage,
|
|
2155
|
+
goToPage: (page) => {
|
|
2156
|
+
showPage(page);
|
|
2157
|
+
},
|
|
1843
2158
|
zoomIn: () => {
|
|
1844
|
-
scale += 0.
|
|
1845
|
-
|
|
2159
|
+
scale += 0.15;
|
|
2160
|
+
applyTransform();
|
|
1846
2161
|
},
|
|
1847
2162
|
zoomOut: () => {
|
|
1848
|
-
scale = Math.max(0.2, scale - 0.
|
|
1849
|
-
|
|
2163
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
2164
|
+
applyTransform();
|
|
1850
2165
|
},
|
|
1851
2166
|
getZoom: () => scale,
|
|
1852
2167
|
setZoom: (level) => {
|
|
1853
2168
|
scale = level;
|
|
1854
|
-
|
|
2169
|
+
applyTransform();
|
|
1855
2170
|
},
|
|
1856
2171
|
fitToPage: () => {
|
|
1857
|
-
scale =
|
|
1858
|
-
|
|
2172
|
+
scale = calculateFitScale();
|
|
2173
|
+
rotation = 0;
|
|
2174
|
+
applyTransform();
|
|
2175
|
+
},
|
|
2176
|
+
rotateCW: () => {
|
|
2177
|
+
rotation = (rotation + 90) % 360;
|
|
2178
|
+
applyTransform();
|
|
2179
|
+
},
|
|
2180
|
+
rotateCCW: () => {
|
|
2181
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
2182
|
+
applyTransform();
|
|
1859
2183
|
},
|
|
1860
2184
|
download: () => {
|
|
1861
2185
|
const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
|
|
@@ -2104,7 +2428,31 @@ var ExcelPlugin = class {
|
|
|
2104
2428
|
return this.mimeTypes.includes(mime || "");
|
|
2105
2429
|
}
|
|
2106
2430
|
getToolbarActions(instance) {
|
|
2107
|
-
|
|
2431
|
+
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2432
|
+
const actions = [];
|
|
2433
|
+
if (totalSheets > 1) {
|
|
2434
|
+
actions.push({
|
|
2435
|
+
id: "page-nav",
|
|
2436
|
+
icon: "",
|
|
2437
|
+
label: "Sheet Navigation",
|
|
2438
|
+
type: "page-nav",
|
|
2439
|
+
group: "navigation",
|
|
2440
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2441
|
+
max: totalSheets,
|
|
2442
|
+
execute: (action, sheet) => {
|
|
2443
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2444
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2445
|
+
if (action === "prev") {
|
|
2446
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2447
|
+
} else if (action === "next") {
|
|
2448
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2449
|
+
} else if (typeof sheet === "number") {
|
|
2450
|
+
instance.goToPage?.(sheet);
|
|
2451
|
+
}
|
|
2452
|
+
}
|
|
2453
|
+
});
|
|
2454
|
+
}
|
|
2455
|
+
actions.push(
|
|
2108
2456
|
{
|
|
2109
2457
|
id: "zoom-out",
|
|
2110
2458
|
icon: "zoom-out",
|
|
@@ -2126,13 +2474,23 @@ var ExcelPlugin = class {
|
|
|
2126
2474
|
}
|
|
2127
2475
|
},
|
|
2128
2476
|
{
|
|
2129
|
-
id: "page
|
|
2130
|
-
icon: "page
|
|
2131
|
-
label: "
|
|
2132
|
-
type: "
|
|
2133
|
-
group: "
|
|
2134
|
-
execute: (
|
|
2135
|
-
|
|
2477
|
+
id: "fit-page",
|
|
2478
|
+
icon: "fit-page",
|
|
2479
|
+
label: "Fit to View",
|
|
2480
|
+
type: "button",
|
|
2481
|
+
group: "zoom",
|
|
2482
|
+
execute: () => {
|
|
2483
|
+
instance.fitToPage?.();
|
|
2484
|
+
}
|
|
2485
|
+
},
|
|
2486
|
+
{
|
|
2487
|
+
id: "rotate-cw",
|
|
2488
|
+
icon: "rotate-cw",
|
|
2489
|
+
label: "Rotate",
|
|
2490
|
+
type: "button",
|
|
2491
|
+
group: "view",
|
|
2492
|
+
execute: () => {
|
|
2493
|
+
instance.rotateCW?.();
|
|
2136
2494
|
}
|
|
2137
2495
|
},
|
|
2138
2496
|
{
|
|
@@ -2155,7 +2513,8 @@ var ExcelPlugin = class {
|
|
|
2155
2513
|
instance.print?.();
|
|
2156
2514
|
}
|
|
2157
2515
|
}
|
|
2158
|
-
|
|
2516
|
+
);
|
|
2517
|
+
return actions;
|
|
2159
2518
|
}
|
|
2160
2519
|
async render(ctx) {
|
|
2161
2520
|
const container = document.createElement("div");
|
|
@@ -2184,9 +2543,23 @@ var ExcelPlugin = class {
|
|
|
2184
2543
|
container.appendChild(tabsArea);
|
|
2185
2544
|
ctx.container.appendChild(container);
|
|
2186
2545
|
let scale = 1;
|
|
2546
|
+
let rotation = 0;
|
|
2187
2547
|
let currentSheetIndex = 1;
|
|
2188
2548
|
let sheetNames = [];
|
|
2189
2549
|
let wb = null;
|
|
2550
|
+
const applyTransform = () => {
|
|
2551
|
+
contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
2552
|
+
contentArea.style.transformOrigin = "top left";
|
|
2553
|
+
};
|
|
2554
|
+
const calculateFitScale = () => {
|
|
2555
|
+
const table = contentArea.querySelector("table");
|
|
2556
|
+
if (!table) return 1;
|
|
2557
|
+
const availW = Math.max(200, container.clientWidth - 48);
|
|
2558
|
+
const availH = Math.max(200, container.clientHeight - 80);
|
|
2559
|
+
const tW = table.offsetWidth || 800;
|
|
2560
|
+
const tH = table.offsetHeight || 600;
|
|
2561
|
+
return Math.min(1, Math.min(availW / tW, availH / tH));
|
|
2562
|
+
};
|
|
2190
2563
|
try {
|
|
2191
2564
|
wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
|
|
2192
2565
|
sheetNames = wb.SheetNames || [];
|
|
@@ -2239,6 +2612,7 @@ var ExcelPlugin = class {
|
|
|
2239
2612
|
b.style.fontWeight = "normal";
|
|
2240
2613
|
}
|
|
2241
2614
|
});
|
|
2615
|
+
ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
|
|
2242
2616
|
};
|
|
2243
2617
|
if (sheetNames.length > 0) {
|
|
2244
2618
|
sheetNames.forEach((name, idx) => {
|
|
@@ -2273,17 +2647,30 @@ var ExcelPlugin = class {
|
|
|
2273
2647
|
return {
|
|
2274
2648
|
destroy: cleanup,
|
|
2275
2649
|
zoomIn: () => {
|
|
2276
|
-
scale += 0.
|
|
2277
|
-
|
|
2650
|
+
scale += 0.15;
|
|
2651
|
+
applyTransform();
|
|
2278
2652
|
},
|
|
2279
2653
|
zoomOut: () => {
|
|
2280
|
-
scale = Math.max(0.2, scale - 0.
|
|
2281
|
-
|
|
2654
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
2655
|
+
applyTransform();
|
|
2282
2656
|
},
|
|
2283
2657
|
getZoom: () => scale,
|
|
2284
2658
|
setZoom: (level) => {
|
|
2285
2659
|
scale = level;
|
|
2286
|
-
|
|
2660
|
+
applyTransform();
|
|
2661
|
+
},
|
|
2662
|
+
fitToPage: () => {
|
|
2663
|
+
scale = calculateFitScale();
|
|
2664
|
+
rotation = 0;
|
|
2665
|
+
applyTransform();
|
|
2666
|
+
},
|
|
2667
|
+
rotateCW: () => {
|
|
2668
|
+
rotation = (rotation + 90) % 360;
|
|
2669
|
+
applyTransform();
|
|
2670
|
+
},
|
|
2671
|
+
rotateCCW: () => {
|
|
2672
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
2673
|
+
applyTransform();
|
|
2287
2674
|
},
|
|
2288
2675
|
goToPage: (page) => {
|
|
2289
2676
|
if (page > 0 && page <= sheetNames.length) {
|
|
@@ -2488,7 +2875,31 @@ var CodePlugin = class {
|
|
|
2488
2875
|
return false;
|
|
2489
2876
|
}
|
|
2490
2877
|
getToolbarActions(instance) {
|
|
2491
|
-
|
|
2878
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2879
|
+
const actions = [];
|
|
2880
|
+
if (totalPages > 1) {
|
|
2881
|
+
actions.push({
|
|
2882
|
+
id: "page-nav",
|
|
2883
|
+
icon: "",
|
|
2884
|
+
label: "Page Navigation",
|
|
2885
|
+
type: "page-nav",
|
|
2886
|
+
group: "navigation",
|
|
2887
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2888
|
+
max: totalPages,
|
|
2889
|
+
execute: (action, page) => {
|
|
2890
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2891
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2892
|
+
if (action === "prev") {
|
|
2893
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2894
|
+
} else if (action === "next") {
|
|
2895
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2896
|
+
} else if (typeof page === "number") {
|
|
2897
|
+
instance.goToPage?.(page);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2902
|
+
actions.push(
|
|
2492
2903
|
{
|
|
2493
2904
|
id: "zoom-out",
|
|
2494
2905
|
icon: "zoom-out",
|
|
@@ -2509,6 +2920,26 @@ var CodePlugin = class {
|
|
|
2509
2920
|
instance.zoomIn?.();
|
|
2510
2921
|
}
|
|
2511
2922
|
},
|
|
2923
|
+
{
|
|
2924
|
+
id: "fit-page",
|
|
2925
|
+
icon: "fit-page",
|
|
2926
|
+
label: "Fit to View",
|
|
2927
|
+
type: "button",
|
|
2928
|
+
group: "zoom",
|
|
2929
|
+
execute: () => {
|
|
2930
|
+
instance.fitToPage?.();
|
|
2931
|
+
}
|
|
2932
|
+
},
|
|
2933
|
+
{
|
|
2934
|
+
id: "rotate-cw",
|
|
2935
|
+
icon: "rotate-cw",
|
|
2936
|
+
label: "Rotate",
|
|
2937
|
+
type: "button",
|
|
2938
|
+
group: "view",
|
|
2939
|
+
execute: () => {
|
|
2940
|
+
instance.rotateCW?.();
|
|
2941
|
+
}
|
|
2942
|
+
},
|
|
2512
2943
|
{
|
|
2513
2944
|
id: "copy",
|
|
2514
2945
|
icon: "copy",
|
|
@@ -2539,11 +2970,16 @@ var CodePlugin = class {
|
|
|
2539
2970
|
instance.print?.();
|
|
2540
2971
|
}
|
|
2541
2972
|
}
|
|
2542
|
-
|
|
2973
|
+
);
|
|
2974
|
+
return actions;
|
|
2543
2975
|
}
|
|
2544
2976
|
async render(ctx) {
|
|
2545
2977
|
const decoder = new TextDecoder("utf-8");
|
|
2546
|
-
const
|
|
2978
|
+
const fullText = decoder.decode(ctx.buffer);
|
|
2979
|
+
const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
|
|
2980
|
+
const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
2981
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
2982
|
+
let currentPage = 1;
|
|
2547
2983
|
const container = document.createElement("div");
|
|
2548
2984
|
container.style.width = "100%";
|
|
2549
2985
|
container.style.height = "100%";
|
|
@@ -2553,6 +2989,7 @@ var CodePlugin = class {
|
|
|
2553
2989
|
container.style.padding = "16px";
|
|
2554
2990
|
container.style.boxSizing = "border-box";
|
|
2555
2991
|
let fontSize = 13;
|
|
2992
|
+
let rotation = 0;
|
|
2556
2993
|
const pre = document.createElement("pre");
|
|
2557
2994
|
pre.style.margin = "0";
|
|
2558
2995
|
pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
|
|
@@ -2562,25 +2999,66 @@ var CodePlugin = class {
|
|
|
2562
2999
|
pre.style.wordBreak = "break-all";
|
|
2563
3000
|
const code = document.createElement("code");
|
|
2564
3001
|
const ext = (ctx.metadata.extension || "").replace(".", "");
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
3002
|
+
const renderCodePage = (text) => {
|
|
3003
|
+
try {
|
|
3004
|
+
if (ext && hljs.getLanguage(ext)) {
|
|
3005
|
+
code.innerHTML = hljs.highlight(text, { language: ext }).value;
|
|
3006
|
+
} else {
|
|
3007
|
+
code.innerHTML = hljs.highlightAuto(text).value;
|
|
3008
|
+
}
|
|
3009
|
+
} catch {
|
|
3010
|
+
code.textContent = text;
|
|
2570
3011
|
}
|
|
2571
|
-
}
|
|
2572
|
-
|
|
2573
|
-
}
|
|
3012
|
+
};
|
|
3013
|
+
renderCodePage(rawPages[0] || fullText);
|
|
2574
3014
|
pre.appendChild(code);
|
|
2575
3015
|
container.appendChild(pre);
|
|
3016
|
+
let indicator = null;
|
|
3017
|
+
if (totalPages > 1) {
|
|
3018
|
+
indicator = document.createElement("div");
|
|
3019
|
+
indicator.className = "fp-code-page-indicator";
|
|
3020
|
+
indicator.style.position = "sticky";
|
|
3021
|
+
indicator.style.bottom = "16px";
|
|
3022
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3023
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
3024
|
+
indicator.style.color = "#f8fafc";
|
|
3025
|
+
indicator.style.fontSize = "12px";
|
|
3026
|
+
indicator.style.fontWeight = "600";
|
|
3027
|
+
indicator.style.padding = "5px 14px";
|
|
3028
|
+
indicator.style.borderRadius = "20px";
|
|
3029
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3030
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3031
|
+
indicator.style.zIndex = "10";
|
|
3032
|
+
indicator.style.userSelect = "none";
|
|
3033
|
+
indicator.style.pointerEvents = "none";
|
|
3034
|
+
indicator.style.textAlign = "center";
|
|
3035
|
+
indicator.style.width = "fit-content";
|
|
3036
|
+
indicator.style.margin = "16px auto 0";
|
|
3037
|
+
container.appendChild(indicator);
|
|
3038
|
+
}
|
|
3039
|
+
const showPage = (pageNum) => {
|
|
3040
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3041
|
+
renderCodePage(rawPages[currentPage - 1] || fullText);
|
|
3042
|
+
if (indicator) {
|
|
3043
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3044
|
+
}
|
|
3045
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3046
|
+
};
|
|
3047
|
+
if (totalPages > 1) {
|
|
3048
|
+
showPage(1);
|
|
3049
|
+
}
|
|
2576
3050
|
ctx.container.appendChild(container);
|
|
2577
3051
|
const cleanup = () => {
|
|
3052
|
+
indicator?.remove();
|
|
2578
3053
|
container.remove();
|
|
2579
3054
|
ctx.container.innerHTML = "";
|
|
2580
3055
|
};
|
|
2581
3056
|
ctx.signal.addEventListener("abort", cleanup);
|
|
2582
3057
|
return {
|
|
2583
3058
|
destroy: cleanup,
|
|
3059
|
+
getPageCount: () => totalPages,
|
|
3060
|
+
getCurrentPage: () => currentPage,
|
|
3061
|
+
goToPage: (page) => showPage(page),
|
|
2584
3062
|
zoomIn: () => {
|
|
2585
3063
|
fontSize = Math.min(32, fontSize + 2);
|
|
2586
3064
|
pre.style.fontSize = `${fontSize}px`;
|
|
@@ -2594,6 +3072,22 @@ var CodePlugin = class {
|
|
|
2594
3072
|
fontSize = Math.round(13 * level);
|
|
2595
3073
|
pre.style.fontSize = `${fontSize}px`;
|
|
2596
3074
|
},
|
|
3075
|
+
fitToPage: () => {
|
|
3076
|
+
fontSize = 13;
|
|
3077
|
+
rotation = 0;
|
|
3078
|
+
pre.style.fontSize = "13px";
|
|
3079
|
+
pre.style.transform = "none";
|
|
3080
|
+
},
|
|
3081
|
+
rotateCW: () => {
|
|
3082
|
+
rotation = (rotation + 90) % 360;
|
|
3083
|
+
pre.style.transform = `rotate(${rotation}deg)`;
|
|
3084
|
+
pre.style.transformOrigin = "top left";
|
|
3085
|
+
},
|
|
3086
|
+
rotateCCW: () => {
|
|
3087
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
3088
|
+
pre.style.transform = `rotate(${rotation}deg)`;
|
|
3089
|
+
pre.style.transformOrigin = "top left";
|
|
3090
|
+
},
|
|
2597
3091
|
download: () => {
|
|
2598
3092
|
const mimeType = ctx.metadata.mimeType || "text/plain";
|
|
2599
3093
|
const blob = new Blob([ctx.buffer], { type: mimeType });
|
|
@@ -2608,7 +3102,7 @@ var CodePlugin = class {
|
|
|
2608
3102
|
window.print();
|
|
2609
3103
|
},
|
|
2610
3104
|
copy: () => {
|
|
2611
|
-
navigator.clipboard?.writeText(
|
|
3105
|
+
navigator.clipboard?.writeText(fullText);
|
|
2612
3106
|
}
|
|
2613
3107
|
};
|
|
2614
3108
|
}
|
|
@@ -3105,6 +3599,14 @@ var PptxPlugin = class {
|
|
|
3105
3599
|
group: "zoom",
|
|
3106
3600
|
execute: () => instance.fitToPage?.()
|
|
3107
3601
|
},
|
|
3602
|
+
{
|
|
3603
|
+
id: "rotate-cw",
|
|
3604
|
+
icon: "rotate-cw",
|
|
3605
|
+
label: "Rotate",
|
|
3606
|
+
type: "button",
|
|
3607
|
+
group: "view",
|
|
3608
|
+
execute: () => instance.rotateCW?.()
|
|
3609
|
+
},
|
|
3108
3610
|
{
|
|
3109
3611
|
id: "download",
|
|
3110
3612
|
icon: "download",
|
|
@@ -3129,6 +3631,7 @@ var PptxPlugin = class {
|
|
|
3129
3631
|
const slideCount = renderer.slidePaths?.length || 1;
|
|
3130
3632
|
let currentSlide = 1;
|
|
3131
3633
|
let scale = 1;
|
|
3634
|
+
let rotation = 0;
|
|
3132
3635
|
const wrapper = document.createElement("div");
|
|
3133
3636
|
wrapper.className = "fp-pptx-wrapper";
|
|
3134
3637
|
wrapper.style.cssText = `
|
|
@@ -3151,6 +3654,8 @@ var PptxPlugin = class {
|
|
|
3151
3654
|
background: #ffffff;
|
|
3152
3655
|
transform-origin: top center;
|
|
3153
3656
|
transition: transform 0.2s ease;
|
|
3657
|
+
max-width: calc(100% - 32px);
|
|
3658
|
+
max-height: calc(100% - 48px);
|
|
3154
3659
|
`;
|
|
3155
3660
|
const canvas = document.createElement("canvas");
|
|
3156
3661
|
slideContainer.appendChild(canvas);
|
|
@@ -3158,10 +3663,22 @@ var PptxPlugin = class {
|
|
|
3158
3663
|
ctx.container.innerHTML = "";
|
|
3159
3664
|
ctx.container.style.overflow = "auto";
|
|
3160
3665
|
ctx.container.appendChild(wrapper);
|
|
3666
|
+
const calculateFitScale = () => {
|
|
3667
|
+
const availW = Math.max(200, ctx.container.clientWidth - 48);
|
|
3668
|
+
const availH = Math.max(200, ctx.container.clientHeight - 72);
|
|
3669
|
+
const cW = canvas.offsetWidth || 1280;
|
|
3670
|
+
const cH = canvas.offsetHeight || 720;
|
|
3671
|
+
return Math.min(1, Math.min(availW / cW, availH / cH));
|
|
3672
|
+
};
|
|
3673
|
+
const applyTransform = () => {
|
|
3674
|
+
slideContainer.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
3675
|
+
};
|
|
3161
3676
|
const renderCurrentSlide = async () => {
|
|
3162
3677
|
try {
|
|
3163
3678
|
await renderer.renderSlide(currentSlide - 1, canvas, 1280);
|
|
3164
3679
|
ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
|
|
3680
|
+
scale = calculateFitScale();
|
|
3681
|
+
applyTransform();
|
|
3165
3682
|
} catch (err) {
|
|
3166
3683
|
console.error("[PptxPlugin] Failed to render slide:", err);
|
|
3167
3684
|
}
|
|
@@ -3184,21 +3701,30 @@ var PptxPlugin = class {
|
|
|
3184
3701
|
getPageCount: () => slideCount,
|
|
3185
3702
|
getCurrentPage: () => currentSlide,
|
|
3186
3703
|
zoomIn: () => {
|
|
3187
|
-
scale += 0.
|
|
3188
|
-
|
|
3704
|
+
scale += 0.15;
|
|
3705
|
+
applyTransform();
|
|
3189
3706
|
},
|
|
3190
3707
|
zoomOut: () => {
|
|
3191
|
-
scale = Math.max(0.2, scale - 0.
|
|
3192
|
-
|
|
3708
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
3709
|
+
applyTransform();
|
|
3193
3710
|
},
|
|
3194
3711
|
getZoom: () => scale,
|
|
3195
3712
|
setZoom: (level) => {
|
|
3196
3713
|
scale = level;
|
|
3197
|
-
|
|
3714
|
+
applyTransform();
|
|
3198
3715
|
},
|
|
3199
3716
|
fitToPage: () => {
|
|
3200
|
-
scale =
|
|
3201
|
-
|
|
3717
|
+
scale = calculateFitScale();
|
|
3718
|
+
rotation = 0;
|
|
3719
|
+
applyTransform();
|
|
3720
|
+
},
|
|
3721
|
+
rotateCW: () => {
|
|
3722
|
+
rotation = (rotation + 90) % 360;
|
|
3723
|
+
applyTransform();
|
|
3724
|
+
},
|
|
3725
|
+
rotateCCW: () => {
|
|
3726
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
3727
|
+
applyTransform();
|
|
3202
3728
|
},
|
|
3203
3729
|
getThumbnails: () => {
|
|
3204
3730
|
const list = [];
|
|
@@ -3430,7 +3956,31 @@ var RtfPlugin = class {
|
|
|
3430
3956
|
return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
|
|
3431
3957
|
}
|
|
3432
3958
|
getToolbarActions(instance) {
|
|
3433
|
-
|
|
3959
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3960
|
+
const actions = [];
|
|
3961
|
+
if (totalPages > 1) {
|
|
3962
|
+
actions.push({
|
|
3963
|
+
id: "page-nav",
|
|
3964
|
+
icon: "",
|
|
3965
|
+
label: "Page Navigation",
|
|
3966
|
+
type: "page-nav",
|
|
3967
|
+
group: "navigation",
|
|
3968
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3969
|
+
max: totalPages,
|
|
3970
|
+
execute: (action, page) => {
|
|
3971
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3972
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3973
|
+
if (action === "prev") {
|
|
3974
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3975
|
+
} else if (action === "next") {
|
|
3976
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3977
|
+
} else if (typeof page === "number") {
|
|
3978
|
+
instance.goToPage?.(page);
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
});
|
|
3982
|
+
}
|
|
3983
|
+
actions.push(
|
|
3434
3984
|
{
|
|
3435
3985
|
id: "zoom-out",
|
|
3436
3986
|
icon: "zoom-out",
|
|
@@ -3455,6 +4005,14 @@ var RtfPlugin = class {
|
|
|
3455
4005
|
group: "zoom",
|
|
3456
4006
|
execute: () => instance.fitToPage?.()
|
|
3457
4007
|
},
|
|
4008
|
+
{
|
|
4009
|
+
id: "rotate-cw",
|
|
4010
|
+
icon: "rotate-cw",
|
|
4011
|
+
label: "Rotate",
|
|
4012
|
+
type: "button",
|
|
4013
|
+
group: "view",
|
|
4014
|
+
execute: () => instance.rotateCW?.()
|
|
4015
|
+
},
|
|
3458
4016
|
{
|
|
3459
4017
|
id: "download",
|
|
3460
4018
|
icon: "download",
|
|
@@ -3471,7 +4029,8 @@ var RtfPlugin = class {
|
|
|
3471
4029
|
group: "actions",
|
|
3472
4030
|
execute: () => instance.print?.()
|
|
3473
4031
|
}
|
|
3474
|
-
|
|
4032
|
+
);
|
|
4033
|
+
return actions;
|
|
3475
4034
|
}
|
|
3476
4035
|
async render(ctx) {
|
|
3477
4036
|
const wrapper = document.createElement("div");
|
|
@@ -3490,44 +4049,124 @@ var RtfPlugin = class {
|
|
|
3490
4049
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
3491
4050
|
ctx.container.appendChild(wrapper);
|
|
3492
4051
|
let scale = 1;
|
|
4052
|
+
let rotation = 0;
|
|
4053
|
+
let pageElements = [];
|
|
4054
|
+
const calculateFitScale = () => {
|
|
4055
|
+
const activeEl = pageElements[currentPage - 1] || wrapper;
|
|
4056
|
+
const elW = activeEl.offsetWidth || 850;
|
|
4057
|
+
const elH = activeEl.offsetHeight || 1e3;
|
|
4058
|
+
const availW = Math.max(200, ctx.container.clientWidth - 48);
|
|
4059
|
+
const availH = Math.max(200, ctx.container.clientHeight - 80);
|
|
4060
|
+
return Math.min(1.1, Math.min(availW / elW, availH / elH));
|
|
4061
|
+
};
|
|
4062
|
+
const applyTransform = () => {
|
|
4063
|
+
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
4064
|
+
wrapper.style.transformOrigin = "top center";
|
|
4065
|
+
};
|
|
4066
|
+
setTimeout(() => {
|
|
4067
|
+
scale = calculateFitScale();
|
|
4068
|
+
applyTransform();
|
|
4069
|
+
}, 60);
|
|
3493
4070
|
try {
|
|
3494
4071
|
if (typeof RTFJS.loggingEnabled === "function") {
|
|
3495
4072
|
RTFJS.loggingEnabled(false);
|
|
3496
4073
|
}
|
|
3497
4074
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
3498
4075
|
const htmlElements = await doc.render();
|
|
3499
|
-
|
|
4076
|
+
pageElements = htmlElements;
|
|
4077
|
+
for (let i = 0; i < htmlElements.length; i++) {
|
|
4078
|
+
const el = htmlElements[i];
|
|
4079
|
+
el.style.display = i === 0 ? "block" : "none";
|
|
3500
4080
|
wrapper.appendChild(el);
|
|
3501
4081
|
}
|
|
3502
4082
|
} catch (err) {
|
|
3503
4083
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
3504
4084
|
const text = new TextDecoder("latin1").decode(ctx.buffer);
|
|
3505
4085
|
const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
|
|
3506
|
-
|
|
4086
|
+
const pre = document.createElement("pre");
|
|
4087
|
+
pre.style.whiteSpace = "pre-wrap";
|
|
4088
|
+
pre.style.fontFamily = "serif";
|
|
4089
|
+
pre.style.color = "#333";
|
|
4090
|
+
pre.textContent = clean;
|
|
4091
|
+
wrapper.appendChild(pre);
|
|
4092
|
+
pageElements = [pre];
|
|
4093
|
+
}
|
|
4094
|
+
const totalPages = Math.max(1, pageElements.length);
|
|
4095
|
+
let currentPage = 1;
|
|
4096
|
+
let indicator = null;
|
|
4097
|
+
if (totalPages > 1) {
|
|
4098
|
+
indicator = document.createElement("div");
|
|
4099
|
+
indicator.className = "fp-rtf-page-indicator";
|
|
4100
|
+
indicator.style.position = "sticky";
|
|
4101
|
+
indicator.style.bottom = "16px";
|
|
4102
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4103
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4104
|
+
indicator.style.color = "#f8fafc";
|
|
4105
|
+
indicator.style.fontSize = "12px";
|
|
4106
|
+
indicator.style.fontWeight = "600";
|
|
4107
|
+
indicator.style.padding = "5px 14px";
|
|
4108
|
+
indicator.style.borderRadius = "20px";
|
|
4109
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4110
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4111
|
+
indicator.style.zIndex = "10";
|
|
4112
|
+
indicator.style.userSelect = "none";
|
|
4113
|
+
indicator.style.pointerEvents = "none";
|
|
4114
|
+
indicator.style.textAlign = "center";
|
|
4115
|
+
indicator.style.width = "fit-content";
|
|
4116
|
+
indicator.style.margin = "16px auto 0";
|
|
4117
|
+
ctx.container.appendChild(indicator);
|
|
4118
|
+
}
|
|
4119
|
+
const showPage = (pageNum) => {
|
|
4120
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4121
|
+
if (totalPages > 1) {
|
|
4122
|
+
pageElements.forEach((el, idx) => {
|
|
4123
|
+
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4124
|
+
});
|
|
4125
|
+
}
|
|
4126
|
+
if (indicator) {
|
|
4127
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4128
|
+
}
|
|
4129
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4130
|
+
};
|
|
4131
|
+
if (totalPages > 1) {
|
|
4132
|
+
showPage(1);
|
|
3507
4133
|
}
|
|
3508
4134
|
const cleanup = () => {
|
|
4135
|
+
indicator?.remove();
|
|
3509
4136
|
wrapper.remove();
|
|
3510
4137
|
ctx.container.innerHTML = "";
|
|
3511
4138
|
};
|
|
3512
4139
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3513
4140
|
return {
|
|
3514
4141
|
destroy: cleanup,
|
|
4142
|
+
getPageCount: () => totalPages,
|
|
4143
|
+
getCurrentPage: () => currentPage,
|
|
4144
|
+
goToPage: (page) => showPage(page),
|
|
3515
4145
|
zoomIn: () => {
|
|
3516
|
-
scale += 0.
|
|
3517
|
-
|
|
4146
|
+
scale += 0.15;
|
|
4147
|
+
applyTransform();
|
|
3518
4148
|
},
|
|
3519
4149
|
zoomOut: () => {
|
|
3520
|
-
scale = Math.max(0.2, scale - 0.
|
|
3521
|
-
|
|
4150
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
4151
|
+
applyTransform();
|
|
3522
4152
|
},
|
|
3523
4153
|
getZoom: () => scale,
|
|
3524
4154
|
setZoom: (level) => {
|
|
3525
4155
|
scale = level;
|
|
3526
|
-
|
|
4156
|
+
applyTransform();
|
|
3527
4157
|
},
|
|
3528
4158
|
fitToPage: () => {
|
|
3529
|
-
scale =
|
|
3530
|
-
|
|
4159
|
+
scale = calculateFitScale();
|
|
4160
|
+
rotation = 0;
|
|
4161
|
+
applyTransform();
|
|
4162
|
+
},
|
|
4163
|
+
rotateCW: () => {
|
|
4164
|
+
rotation = (rotation + 90) % 360;
|
|
4165
|
+
applyTransform();
|
|
4166
|
+
},
|
|
4167
|
+
rotateCCW: () => {
|
|
4168
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
4169
|
+
applyTransform();
|
|
3531
4170
|
},
|
|
3532
4171
|
download: () => {
|
|
3533
4172
|
const blob = new Blob([ctx.buffer], { type: "application/rtf" });
|
|
@@ -3699,41 +4338,41 @@ var OpenDocumentPlugin = class {
|
|
|
3699
4338
|
}
|
|
3700
4339
|
getToolbarActions(instance) {
|
|
3701
4340
|
const isPresentation = instance.isPresentation;
|
|
4341
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3702
4342
|
const actions = [];
|
|
3703
|
-
if (isPresentation) {
|
|
3704
|
-
|
|
3705
|
-
{
|
|
4343
|
+
if (isPresentation || totalPages > 1) {
|
|
4344
|
+
if (isPresentation) {
|
|
4345
|
+
actions.push({
|
|
3706
4346
|
id: "thumbnails",
|
|
3707
4347
|
icon: "thumbnails",
|
|
3708
4348
|
label: "Slide Thumbnails",
|
|
3709
4349
|
type: "button",
|
|
3710
4350
|
group: "navigation",
|
|
3711
4351
|
execute: () => instance.toggleThumbnails?.()
|
|
3712
|
-
}
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
}
|
|
4352
|
+
});
|
|
4353
|
+
}
|
|
4354
|
+
actions.push({
|
|
4355
|
+
id: "page-nav",
|
|
4356
|
+
icon: "",
|
|
4357
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
4358
|
+
type: "page-nav",
|
|
4359
|
+
group: "navigation",
|
|
4360
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4361
|
+
max: totalPages,
|
|
4362
|
+
execute: (action, page) => {
|
|
4363
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4364
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4365
|
+
if (action === "prev") {
|
|
4366
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4367
|
+
} else if (action === "next") {
|
|
4368
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4369
|
+
} else if (typeof page === "number") {
|
|
4370
|
+
instance.goToPage?.(page);
|
|
4371
|
+
} else if (typeof action === "number") {
|
|
4372
|
+
instance.goToPage?.(action);
|
|
3734
4373
|
}
|
|
3735
4374
|
}
|
|
3736
|
-
);
|
|
4375
|
+
});
|
|
3737
4376
|
}
|
|
3738
4377
|
actions.push(
|
|
3739
4378
|
{
|
|
@@ -3755,11 +4394,19 @@ var OpenDocumentPlugin = class {
|
|
|
3755
4394
|
{
|
|
3756
4395
|
id: "fit-page",
|
|
3757
4396
|
icon: "fit-page",
|
|
3758
|
-
label: "Fit to Page",
|
|
4397
|
+
label: isPresentation ? "Fit to Slide" : "Fit to Page",
|
|
3759
4398
|
type: "button",
|
|
3760
4399
|
group: "zoom",
|
|
3761
4400
|
execute: () => instance.fitToPage?.()
|
|
3762
4401
|
},
|
|
4402
|
+
{
|
|
4403
|
+
id: "rotate-cw",
|
|
4404
|
+
icon: "rotate-cw",
|
|
4405
|
+
label: "Rotate",
|
|
4406
|
+
type: "button",
|
|
4407
|
+
group: "view",
|
|
4408
|
+
execute: () => instance.rotateCW?.()
|
|
4409
|
+
},
|
|
3763
4410
|
{
|
|
3764
4411
|
id: "download",
|
|
3765
4412
|
icon: "download",
|
|
@@ -3823,6 +4470,22 @@ var OpenDocumentPlugin = class {
|
|
|
3823
4470
|
container.appendChild(wrapper);
|
|
3824
4471
|
ctx.container.appendChild(container);
|
|
3825
4472
|
let scale = 1;
|
|
4473
|
+
let rotation = 0;
|
|
4474
|
+
const calculateFitScale = () => {
|
|
4475
|
+
const elW = wrapper.offsetWidth || 850;
|
|
4476
|
+
const elH = wrapper.offsetHeight || (isPresentation ? 540 : 1e3);
|
|
4477
|
+
const availW = Math.max(200, ctx.container.clientWidth - 48);
|
|
4478
|
+
const availH = Math.max(200, ctx.container.clientHeight - 80);
|
|
4479
|
+
return Math.min(1, Math.min(availW / elW, availH / elH));
|
|
4480
|
+
};
|
|
4481
|
+
const applyTransform = () => {
|
|
4482
|
+
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
4483
|
+
wrapper.style.transformOrigin = "top center";
|
|
4484
|
+
};
|
|
4485
|
+
setTimeout(() => {
|
|
4486
|
+
scale = calculateFitScale();
|
|
4487
|
+
applyTransform();
|
|
4488
|
+
}, 60);
|
|
3826
4489
|
let currentPage = 1;
|
|
3827
4490
|
let totalPages = 1;
|
|
3828
4491
|
let slides = [];
|
|
@@ -3881,21 +4544,30 @@ var OpenDocumentPlugin = class {
|
|
|
3881
4544
|
destroy: cleanup,
|
|
3882
4545
|
isPresentation,
|
|
3883
4546
|
zoomIn: () => {
|
|
3884
|
-
scale += 0.
|
|
3885
|
-
|
|
4547
|
+
scale += 0.15;
|
|
4548
|
+
applyTransform();
|
|
3886
4549
|
},
|
|
3887
4550
|
zoomOut: () => {
|
|
3888
|
-
scale = Math.max(0.2, scale - 0.
|
|
3889
|
-
|
|
4551
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
4552
|
+
applyTransform();
|
|
3890
4553
|
},
|
|
3891
4554
|
getZoom: () => scale,
|
|
3892
4555
|
setZoom: (level) => {
|
|
3893
4556
|
scale = level;
|
|
3894
|
-
|
|
4557
|
+
applyTransform();
|
|
3895
4558
|
},
|
|
3896
4559
|
fitToPage: () => {
|
|
3897
|
-
scale =
|
|
3898
|
-
|
|
4560
|
+
scale = calculateFitScale();
|
|
4561
|
+
rotation = 0;
|
|
4562
|
+
applyTransform();
|
|
4563
|
+
},
|
|
4564
|
+
rotateCW: () => {
|
|
4565
|
+
rotation = (rotation + 90) % 360;
|
|
4566
|
+
applyTransform();
|
|
4567
|
+
},
|
|
4568
|
+
rotateCCW: () => {
|
|
4569
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
4570
|
+
applyTransform();
|
|
3899
4571
|
},
|
|
3900
4572
|
goToPage,
|
|
3901
4573
|
getPageCount: () => totalPages,
|
|
@@ -4116,7 +4788,31 @@ var DocPlugin = class {
|
|
|
4116
4788
|
return this.mimeTypes.includes(mime || "");
|
|
4117
4789
|
}
|
|
4118
4790
|
getToolbarActions(instance) {
|
|
4119
|
-
|
|
4791
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4792
|
+
const actions = [];
|
|
4793
|
+
if (totalPages > 1) {
|
|
4794
|
+
actions.push({
|
|
4795
|
+
id: "page-nav",
|
|
4796
|
+
icon: "",
|
|
4797
|
+
label: "Page Navigation",
|
|
4798
|
+
type: "page-nav",
|
|
4799
|
+
group: "navigation",
|
|
4800
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4801
|
+
max: totalPages,
|
|
4802
|
+
execute: (action, page) => {
|
|
4803
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4804
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4805
|
+
if (action === "prev") {
|
|
4806
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4807
|
+
} else if (action === "next") {
|
|
4808
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4809
|
+
} else if (typeof page === "number") {
|
|
4810
|
+
instance.goToPage?.(page);
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
});
|
|
4814
|
+
}
|
|
4815
|
+
actions.push(
|
|
4120
4816
|
{
|
|
4121
4817
|
id: "zoom-out",
|
|
4122
4818
|
icon: "zoom-out",
|
|
@@ -4141,6 +4837,14 @@ var DocPlugin = class {
|
|
|
4141
4837
|
group: "zoom",
|
|
4142
4838
|
execute: () => instance.fitToPage?.()
|
|
4143
4839
|
},
|
|
4840
|
+
{
|
|
4841
|
+
id: "rotate-cw",
|
|
4842
|
+
icon: "rotate-cw",
|
|
4843
|
+
label: "Rotate",
|
|
4844
|
+
type: "button",
|
|
4845
|
+
group: "view",
|
|
4846
|
+
execute: () => instance.rotateCW?.()
|
|
4847
|
+
},
|
|
4144
4848
|
{
|
|
4145
4849
|
id: "copy",
|
|
4146
4850
|
icon: "copy",
|
|
@@ -4165,7 +4869,8 @@ var DocPlugin = class {
|
|
|
4165
4869
|
group: "actions",
|
|
4166
4870
|
execute: () => instance.print?.()
|
|
4167
4871
|
}
|
|
4168
|
-
|
|
4872
|
+
);
|
|
4873
|
+
return actions;
|
|
4169
4874
|
}
|
|
4170
4875
|
async render(ctx) {
|
|
4171
4876
|
const container = document.createElement("div");
|
|
@@ -4192,6 +4897,7 @@ var DocPlugin = class {
|
|
|
4192
4897
|
ctx.container.appendChild(container);
|
|
4193
4898
|
let scale = 1;
|
|
4194
4899
|
let extractedRawText = "";
|
|
4900
|
+
let isFallback = false;
|
|
4195
4901
|
try {
|
|
4196
4902
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4197
4903
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
@@ -4205,42 +4911,131 @@ var DocPlugin = class {
|
|
|
4205
4911
|
const tableStream = cfbf.readStream(tableName);
|
|
4206
4912
|
const text = this.extractDocText(wordDocStream, tableStream);
|
|
4207
4913
|
extractedRawText = text;
|
|
4208
|
-
wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
|
|
4209
4914
|
} catch (err) {
|
|
4210
4915
|
console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
|
|
4211
4916
|
const fallback = this.heuristicTextExtraction(ctx.buffer);
|
|
4212
4917
|
extractedRawText = fallback;
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4918
|
+
isFallback = true;
|
|
4919
|
+
}
|
|
4920
|
+
const rawPages = this.splitIntoPages(extractedRawText);
|
|
4921
|
+
const totalPages = Math.max(1, rawPages.length);
|
|
4922
|
+
let currentPage = 1;
|
|
4923
|
+
wrapper.innerHTML = "";
|
|
4924
|
+
const pageCards = [];
|
|
4925
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4926
|
+
const pageCard = document.createElement("div");
|
|
4927
|
+
pageCard.className = "fp-doc-page-card";
|
|
4928
|
+
pageCard.style.backgroundColor = "#ffffff";
|
|
4929
|
+
pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
|
|
4930
|
+
pageCard.style.borderRadius = "4px";
|
|
4931
|
+
pageCard.style.padding = "56px 48px";
|
|
4932
|
+
pageCard.style.minHeight = "100%";
|
|
4933
|
+
pageCard.style.display = i === 0 ? "block" : "none";
|
|
4934
|
+
if (isFallback && i === 0) {
|
|
4935
|
+
pageCard.innerHTML = `
|
|
4936
|
+
<div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
|
|
4937
|
+
<h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
|
|
4938
|
+
<span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
|
|
4939
|
+
</div>
|
|
4940
|
+
${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
|
|
4941
|
+
`;
|
|
4942
|
+
} else {
|
|
4943
|
+
pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
|
|
4944
|
+
}
|
|
4945
|
+
wrapper.appendChild(pageCard);
|
|
4946
|
+
pageCards.push(pageCard);
|
|
4947
|
+
}
|
|
4948
|
+
let indicator = null;
|
|
4949
|
+
if (totalPages > 1) {
|
|
4950
|
+
indicator = document.createElement("div");
|
|
4951
|
+
indicator.className = "fp-doc-page-indicator";
|
|
4952
|
+
indicator.style.position = "sticky";
|
|
4953
|
+
indicator.style.bottom = "16px";
|
|
4954
|
+
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4955
|
+
indicator.style.backdropFilter = "blur(8px)";
|
|
4956
|
+
indicator.style.color = "#f8fafc";
|
|
4957
|
+
indicator.style.fontSize = "12px";
|
|
4958
|
+
indicator.style.fontWeight = "600";
|
|
4959
|
+
indicator.style.padding = "5px 14px";
|
|
4960
|
+
indicator.style.borderRadius = "20px";
|
|
4961
|
+
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4962
|
+
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4963
|
+
indicator.style.zIndex = "10";
|
|
4964
|
+
indicator.style.userSelect = "none";
|
|
4965
|
+
indicator.style.pointerEvents = "none";
|
|
4966
|
+
indicator.style.textAlign = "center";
|
|
4967
|
+
indicator.style.width = "fit-content";
|
|
4968
|
+
indicator.style.margin = "16px auto 0";
|
|
4969
|
+
container.appendChild(indicator);
|
|
4970
|
+
}
|
|
4971
|
+
const showPage = (pageNum) => {
|
|
4972
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4973
|
+
if (totalPages > 1) {
|
|
4974
|
+
pageCards.forEach((card, idx) => {
|
|
4975
|
+
card.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
if (indicator) {
|
|
4979
|
+
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4980
|
+
}
|
|
4981
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4982
|
+
};
|
|
4983
|
+
if (totalPages > 1) {
|
|
4984
|
+
showPage(1);
|
|
4220
4985
|
}
|
|
4986
|
+
let rotation = 0;
|
|
4987
|
+
const calculateFitScale = () => {
|
|
4988
|
+
const activeCard = pageCards[currentPage - 1] || wrapper;
|
|
4989
|
+
const elW = activeCard.offsetWidth || 850;
|
|
4990
|
+
const elH = activeCard.offsetHeight || 1e3;
|
|
4991
|
+
const availW = Math.max(200, ctx.container.clientWidth - 48);
|
|
4992
|
+
const availH = Math.max(200, ctx.container.clientHeight - 80);
|
|
4993
|
+
return Math.min(1.1, Math.min(availW / elW, availH / elH));
|
|
4994
|
+
};
|
|
4995
|
+
const applyTransform = () => {
|
|
4996
|
+
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
4997
|
+
wrapper.style.transformOrigin = "top center";
|
|
4998
|
+
};
|
|
4999
|
+
setTimeout(() => {
|
|
5000
|
+
scale = calculateFitScale();
|
|
5001
|
+
applyTransform();
|
|
5002
|
+
}, 60);
|
|
4221
5003
|
const cleanup = () => {
|
|
5004
|
+
indicator?.remove();
|
|
4222
5005
|
container.remove();
|
|
4223
5006
|
ctx.container.innerHTML = "";
|
|
4224
5007
|
};
|
|
4225
5008
|
ctx.signal.addEventListener("abort", cleanup);
|
|
4226
5009
|
return {
|
|
4227
5010
|
destroy: cleanup,
|
|
5011
|
+
getPageCount: () => totalPages,
|
|
5012
|
+
getCurrentPage: () => currentPage,
|
|
5013
|
+
goToPage: (page) => showPage(page),
|
|
4228
5014
|
zoomIn: () => {
|
|
4229
|
-
scale += 0.
|
|
4230
|
-
|
|
5015
|
+
scale += 0.15;
|
|
5016
|
+
applyTransform();
|
|
4231
5017
|
},
|
|
4232
5018
|
zoomOut: () => {
|
|
4233
|
-
scale = Math.max(0.2, scale - 0.
|
|
4234
|
-
|
|
5019
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
5020
|
+
applyTransform();
|
|
4235
5021
|
},
|
|
4236
5022
|
getZoom: () => scale,
|
|
4237
5023
|
setZoom: (level) => {
|
|
4238
5024
|
scale = level;
|
|
4239
|
-
|
|
5025
|
+
applyTransform();
|
|
4240
5026
|
},
|
|
4241
5027
|
fitToPage: () => {
|
|
4242
|
-
scale =
|
|
4243
|
-
|
|
5028
|
+
scale = calculateFitScale();
|
|
5029
|
+
rotation = 0;
|
|
5030
|
+
applyTransform();
|
|
5031
|
+
},
|
|
5032
|
+
rotateCW: () => {
|
|
5033
|
+
rotation = (rotation + 90) % 360;
|
|
5034
|
+
applyTransform();
|
|
5035
|
+
},
|
|
5036
|
+
rotateCCW: () => {
|
|
5037
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
5038
|
+
applyTransform();
|
|
4244
5039
|
},
|
|
4245
5040
|
copy: () => {
|
|
4246
5041
|
navigator.clipboard.writeText(extractedRawText);
|
|
@@ -4356,11 +5151,16 @@ var DocPlugin = class {
|
|
|
4356
5151
|
heuristicTextExtraction(buffer) {
|
|
4357
5152
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
4358
5153
|
}
|
|
5154
|
+
splitIntoPages(text) {
|
|
5155
|
+
if (!text) return [""];
|
|
5156
|
+
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);
|
|
5157
|
+
return parts.length > 0 ? parts : [text];
|
|
5158
|
+
}
|
|
4359
5159
|
/**
|
|
4360
5160
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
4361
5161
|
*/
|
|
4362
5162
|
formatDocToHtml(text, filename) {
|
|
4363
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").
|
|
5163
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
4364
5164
|
let html = "";
|
|
4365
5165
|
let inList = false;
|
|
4366
5166
|
for (const rawLine of lines) {
|
|
@@ -4475,6 +5275,14 @@ var PptPlugin = class {
|
|
|
4475
5275
|
group: "zoom",
|
|
4476
5276
|
execute: () => instance.fitToPage?.()
|
|
4477
5277
|
},
|
|
5278
|
+
{
|
|
5279
|
+
id: "rotate-cw",
|
|
5280
|
+
icon: "rotate-cw",
|
|
5281
|
+
label: "Rotate",
|
|
5282
|
+
type: "button",
|
|
5283
|
+
group: "view",
|
|
5284
|
+
execute: () => instance.rotateCW?.()
|
|
5285
|
+
},
|
|
4478
5286
|
{
|
|
4479
5287
|
id: "download",
|
|
4480
5288
|
icon: "download",
|
|
@@ -4508,22 +5316,36 @@ var PptPlugin = class {
|
|
|
4508
5316
|
const slideCard = document.createElement("div");
|
|
4509
5317
|
slideCard.className = "fp-ppt-slide-card";
|
|
4510
5318
|
slideCard.style.width = "960px";
|
|
4511
|
-
slideCard.style.maxWidth = "
|
|
5319
|
+
slideCard.style.maxWidth = "calc(100% - 32px)";
|
|
5320
|
+
slideCard.style.maxHeight = "calc(100% - 48px)";
|
|
4512
5321
|
slideCard.style.aspectRatio = "16 / 9";
|
|
4513
5322
|
slideCard.style.backgroundColor = "#ffffff";
|
|
4514
5323
|
slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
|
|
4515
5324
|
slideCard.style.borderRadius = "8px";
|
|
4516
5325
|
slideCard.style.boxSizing = "border-box";
|
|
4517
5326
|
slideCard.style.position = "relative";
|
|
4518
|
-
slideCard.style.overflow = "hidden";
|
|
4519
|
-
slideCard.style.transformOrigin = "center center";
|
|
4520
5327
|
slideCard.style.transition = "transform 0.2s ease";
|
|
5328
|
+
slideCard.style.transformOrigin = "center center";
|
|
5329
|
+
slideCard.style.flexShrink = "0";
|
|
4521
5330
|
container.appendChild(slideCard);
|
|
4522
5331
|
ctx.container.appendChild(container);
|
|
4523
5332
|
let scale = 1;
|
|
5333
|
+
let rotation = 0;
|
|
4524
5334
|
let currentSlide = 1;
|
|
4525
5335
|
let slides = [];
|
|
4526
5336
|
const createdBlobUrls = [];
|
|
5337
|
+
const calculateFitScale = () => {
|
|
5338
|
+
const availW = Math.max(200, container.clientWidth - 48);
|
|
5339
|
+
const availH = Math.max(200, container.clientHeight - 72);
|
|
5340
|
+
return Math.min(1, Math.min(availW / 960, availH / 540));
|
|
5341
|
+
};
|
|
5342
|
+
const applyTransform = () => {
|
|
5343
|
+
slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
5344
|
+
};
|
|
5345
|
+
setTimeout(() => {
|
|
5346
|
+
scale = calculateFitScale();
|
|
5347
|
+
applyTransform();
|
|
5348
|
+
}, 60);
|
|
4527
5349
|
try {
|
|
4528
5350
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
4529
5351
|
const pptStream = cfbf.readStream("PowerPoint Document");
|
|
@@ -4624,21 +5446,30 @@ var PptPlugin = class {
|
|
|
4624
5446
|
return {
|
|
4625
5447
|
destroy: cleanup,
|
|
4626
5448
|
zoomIn: () => {
|
|
4627
|
-
scale += 0.
|
|
4628
|
-
|
|
5449
|
+
scale += 0.15;
|
|
5450
|
+
applyTransform();
|
|
4629
5451
|
},
|
|
4630
5452
|
zoomOut: () => {
|
|
4631
|
-
scale = Math.max(0.
|
|
4632
|
-
|
|
5453
|
+
scale = Math.max(0.2, scale - 0.15);
|
|
5454
|
+
applyTransform();
|
|
4633
5455
|
},
|
|
4634
5456
|
getZoom: () => scale,
|
|
4635
5457
|
setZoom: (level) => {
|
|
4636
5458
|
scale = level;
|
|
4637
|
-
|
|
5459
|
+
applyTransform();
|
|
4638
5460
|
},
|
|
4639
5461
|
fitToPage: () => {
|
|
4640
|
-
scale =
|
|
4641
|
-
|
|
5462
|
+
scale = calculateFitScale();
|
|
5463
|
+
rotation = 0;
|
|
5464
|
+
applyTransform();
|
|
5465
|
+
},
|
|
5466
|
+
rotateCW: () => {
|
|
5467
|
+
rotation = (rotation + 90) % 360;
|
|
5468
|
+
applyTransform();
|
|
5469
|
+
},
|
|
5470
|
+
rotateCCW: () => {
|
|
5471
|
+
rotation = (rotation - 90 + 360) % 360;
|
|
5472
|
+
applyTransform();
|
|
4642
5473
|
},
|
|
4643
5474
|
goToPage: (page) => {
|
|
4644
5475
|
if (page >= 1 && page <= totalSlides) {
|