@files-preview-app/preview-file 1.2.8 → 1.3.0
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 +563 -111
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +562 -111
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +606 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +604 -112
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +563 -111
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +562 -111
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +563 -111
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +562 -111
- package/dist/vue.js.map +1 -1
- package/package.json +169 -179
package/dist/index.js
CHANGED
|
@@ -336,16 +336,21 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
336
336
|
metadata.mimeType = source.type || void 0;
|
|
337
337
|
metadata.extension = extractExtension(source.name);
|
|
338
338
|
buffer = await source.arrayBuffer();
|
|
339
|
-
} else if (source instanceof Blob) {
|
|
339
|
+
} else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
|
|
340
340
|
metadata.size = source.size;
|
|
341
341
|
metadata.mimeType = source.type || void 0;
|
|
342
|
+
if (source.name) {
|
|
343
|
+
metadata.name = source.name;
|
|
344
|
+
metadata.extension = extractExtension(source.name);
|
|
345
|
+
}
|
|
342
346
|
buffer = await source.arrayBuffer();
|
|
343
|
-
} else if (source instanceof ArrayBuffer) {
|
|
347
|
+
} else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
|
|
344
348
|
buffer = source;
|
|
345
|
-
} else if (source instanceof Uint8Array) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
+
} else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
|
|
350
|
+
const view = source;
|
|
351
|
+
buffer = view.buffer.slice(
|
|
352
|
+
view.byteOffset,
|
|
353
|
+
view.byteOffset + view.byteLength
|
|
349
354
|
);
|
|
350
355
|
} else {
|
|
351
356
|
throw new Error("Unsupported file source type");
|
|
@@ -474,6 +479,86 @@ function createElement(tag, attrs, ...children) {
|
|
|
474
479
|
}
|
|
475
480
|
return el;
|
|
476
481
|
}
|
|
482
|
+
var DB_NAME = "PreviewFileTransferDB";
|
|
483
|
+
var DB_STORE = "transfers";
|
|
484
|
+
function openDB() {
|
|
485
|
+
return new Promise((resolve, reject) => {
|
|
486
|
+
if (typeof indexedDB === "undefined") {
|
|
487
|
+
return reject(new Error("IndexedDB is not available"));
|
|
488
|
+
}
|
|
489
|
+
const req = indexedDB.open(DB_NAME, 1);
|
|
490
|
+
req.onupgradeneeded = () => {
|
|
491
|
+
const db = req.result;
|
|
492
|
+
if (!db.objectStoreNames.contains(DB_STORE)) {
|
|
493
|
+
db.createObjectStore(DB_STORE, { keyPath: "id" });
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
req.onsuccess = () => resolve(req.result);
|
|
497
|
+
req.onerror = () => reject(req.error);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
async function saveTransferPayload(id, payload) {
|
|
501
|
+
if (typeof window !== "undefined") {
|
|
502
|
+
try {
|
|
503
|
+
window[id] = payload;
|
|
504
|
+
window.__lastTransfer = payload;
|
|
505
|
+
} catch {
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
try {
|
|
509
|
+
const db = await openDB();
|
|
510
|
+
return new Promise((resolve, reject) => {
|
|
511
|
+
const tx = db.transaction(DB_STORE, "readwrite");
|
|
512
|
+
const store = tx.objectStore(DB_STORE);
|
|
513
|
+
store.put({ id, ...payload, timestamp: Date.now() });
|
|
514
|
+
tx.oncomplete = () => resolve();
|
|
515
|
+
tx.onerror = () => reject(tx.error);
|
|
516
|
+
});
|
|
517
|
+
} catch (e) {
|
|
518
|
+
console.warn("[saveTransferPayload] IndexedDB store warning:", e);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
async function getTransferPayload(id) {
|
|
522
|
+
if (typeof window !== "undefined") {
|
|
523
|
+
try {
|
|
524
|
+
if (window.opener && window.opener[id]) {
|
|
525
|
+
return window.opener[id];
|
|
526
|
+
}
|
|
527
|
+
if (window[id]) {
|
|
528
|
+
return window[id];
|
|
529
|
+
}
|
|
530
|
+
if (window.opener && window.opener.__lastTransfer) {
|
|
531
|
+
return window.opener.__lastTransfer;
|
|
532
|
+
}
|
|
533
|
+
if (window.__lastTransfer) {
|
|
534
|
+
return window.__lastTransfer;
|
|
535
|
+
}
|
|
536
|
+
} catch {
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
try {
|
|
540
|
+
const db = await openDB();
|
|
541
|
+
return new Promise((resolve) => {
|
|
542
|
+
const tx = db.transaction(DB_STORE, "readonly");
|
|
543
|
+
const store = tx.objectStore(DB_STORE);
|
|
544
|
+
const req = store.get(id);
|
|
545
|
+
req.onsuccess = () => {
|
|
546
|
+
if (req.result && req.result.buffer) {
|
|
547
|
+
resolve({
|
|
548
|
+
buffer: req.result.buffer,
|
|
549
|
+
metadata: req.result.metadata,
|
|
550
|
+
options: req.result.options
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
resolve(null);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
req.onerror = () => resolve(null);
|
|
557
|
+
});
|
|
558
|
+
} catch {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
477
562
|
var ICON_ZOOM_IN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
|
|
478
563
|
var ICON_ZOOM_OUT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
|
|
479
564
|
var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><polyline points="11 9 8 12 11 15"></polyline><polyline points="13 9 16 12 13 15"></polyline></svg>`;
|
|
@@ -493,6 +578,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
|
|
|
493
578
|
var ICON_FAST_FORWARD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
|
|
494
579
|
var ICON_REWIND = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
|
|
495
580
|
var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
|
|
581
|
+
var ICON_EXTERNAL_WINDOW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`;
|
|
496
582
|
var ICON_MAP = {
|
|
497
583
|
"zoom-in": ICON_ZOOM_IN,
|
|
498
584
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -519,7 +605,9 @@ var ICON_MAP = {
|
|
|
519
605
|
"forward-10": ICON_FAST_FORWARD,
|
|
520
606
|
"rewind": ICON_REWIND,
|
|
521
607
|
"replay-10": ICON_REWIND,
|
|
522
|
-
"speed": ICON_SPEED
|
|
608
|
+
"speed": ICON_SPEED,
|
|
609
|
+
"open-window": ICON_EXTERNAL_WINDOW,
|
|
610
|
+
"external-window": ICON_EXTERNAL_WINDOW
|
|
523
611
|
};
|
|
524
612
|
var ToolbarController = class {
|
|
525
613
|
el;
|
|
@@ -736,7 +824,7 @@ var ThumbnailPanel = class {
|
|
|
736
824
|
}
|
|
737
825
|
}
|
|
738
826
|
};
|
|
739
|
-
var FilePreviewViewer = class {
|
|
827
|
+
var FilePreviewViewer = class _FilePreviewViewer {
|
|
740
828
|
plugins = [];
|
|
741
829
|
activeInstance = null;
|
|
742
830
|
abortController = null;
|
|
@@ -773,6 +861,7 @@ var FilePreviewViewer = class {
|
|
|
773
861
|
* Preview a file in the given container element.
|
|
774
862
|
*/
|
|
775
863
|
async preview(container, source, options = {}) {
|
|
864
|
+
this.currentOptions = options;
|
|
776
865
|
this.abort();
|
|
777
866
|
this.abortController = new AbortController();
|
|
778
867
|
const { signal } = this.abortController;
|
|
@@ -782,7 +871,10 @@ var FilePreviewViewer = class {
|
|
|
782
871
|
this.showLoading();
|
|
783
872
|
try {
|
|
784
873
|
const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
|
|
785
|
-
|
|
874
|
+
if (options.metadata) {
|
|
875
|
+
Object.assign(metadata, options.metadata);
|
|
876
|
+
}
|
|
877
|
+
this.currentBuffer = buffer.slice(0);
|
|
786
878
|
this.currentMetadata = metadata;
|
|
787
879
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
788
880
|
const fileInfo = { metadata, buffer };
|
|
@@ -812,6 +904,7 @@ var FilePreviewViewer = class {
|
|
|
812
904
|
}
|
|
813
905
|
});
|
|
814
906
|
this.activeInstance = instance;
|
|
907
|
+
instance.openInSeparateWindow = () => this.openInSeparateWindow();
|
|
815
908
|
this.hideLoading();
|
|
816
909
|
this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
|
|
817
910
|
if (options.showToolbar !== false && this.toolbar) {
|
|
@@ -821,26 +914,16 @@ var FilePreviewViewer = class {
|
|
|
821
914
|
actions.push({
|
|
822
915
|
id: "fullscreen",
|
|
823
916
|
icon: "fullscreen",
|
|
824
|
-
label: "
|
|
917
|
+
label: "Fullscreen",
|
|
825
918
|
type: "button",
|
|
826
919
|
group: "view",
|
|
827
|
-
execute:
|
|
920
|
+
execute: () => {
|
|
828
921
|
try {
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
if (this.wrapperEl?.requestFullscreen) {
|
|
833
|
-
await this.wrapperEl.requestFullscreen().catch(() => {
|
|
834
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
835
|
-
});
|
|
836
|
-
} else {
|
|
837
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
838
|
-
}
|
|
922
|
+
if (!document.fullscreenElement) {
|
|
923
|
+
this.wrapperEl?.requestFullscreen?.();
|
|
924
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
839
925
|
} else {
|
|
840
|
-
|
|
841
|
-
await document.exitFullscreen().catch(() => {
|
|
842
|
-
});
|
|
843
|
-
}
|
|
926
|
+
document.exitFullscreen?.();
|
|
844
927
|
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
845
928
|
}
|
|
846
929
|
} catch {
|
|
@@ -852,6 +935,28 @@ var FilePreviewViewer = class {
|
|
|
852
935
|
}
|
|
853
936
|
});
|
|
854
937
|
}
|
|
938
|
+
const openWinAction = actions.find((a) => a.id === "open-window");
|
|
939
|
+
if (openWinAction) {
|
|
940
|
+
if (options?._isSeparateWindow) {
|
|
941
|
+
const idx = actions.indexOf(openWinAction);
|
|
942
|
+
if (idx !== -1) actions.splice(idx, 1);
|
|
943
|
+
} else {
|
|
944
|
+
openWinAction.execute = () => {
|
|
945
|
+
this.openInSeparateWindow();
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
} else if (!options?._isSeparateWindow) {
|
|
949
|
+
actions.push({
|
|
950
|
+
id: "open-window",
|
|
951
|
+
icon: "open-window",
|
|
952
|
+
label: "Open in Separate Full Window",
|
|
953
|
+
type: "button",
|
|
954
|
+
group: "actions",
|
|
955
|
+
execute: () => {
|
|
956
|
+
this.openInSeparateWindow();
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}
|
|
855
960
|
this.toolbar.update(actions);
|
|
856
961
|
this.toolbar.show();
|
|
857
962
|
}
|
|
@@ -884,6 +989,104 @@ var FilePreviewViewer = class {
|
|
|
884
989
|
throw error;
|
|
885
990
|
}
|
|
886
991
|
}
|
|
992
|
+
/**
|
|
993
|
+
* Opens the current file preview in a separate full browser window.
|
|
994
|
+
*/
|
|
995
|
+
openInSeparateWindow() {
|
|
996
|
+
if (!this.currentBuffer) {
|
|
997
|
+
console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
if (this.currentOptions.onOpenSeparateWindow) {
|
|
1001
|
+
return this.currentOptions.onOpenSeparateWindow({
|
|
1002
|
+
buffer: this.currentBuffer,
|
|
1003
|
+
metadata: this.currentMetadata || { name: "Document" },
|
|
1004
|
+
options: this.currentOptions
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
|
|
1008
|
+
let clonedBuffer;
|
|
1009
|
+
try {
|
|
1010
|
+
clonedBuffer = this.currentBuffer.slice(0);
|
|
1011
|
+
} catch {
|
|
1012
|
+
clonedBuffer = this.currentBuffer;
|
|
1013
|
+
}
|
|
1014
|
+
const payload = {
|
|
1015
|
+
buffer: clonedBuffer,
|
|
1016
|
+
metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
|
|
1017
|
+
options: { ...this.currentOptions, _isSeparateWindow: true }
|
|
1018
|
+
};
|
|
1019
|
+
if (typeof window !== "undefined") {
|
|
1020
|
+
try {
|
|
1021
|
+
window[transferId] = payload;
|
|
1022
|
+
window.__lastTransfer = payload;
|
|
1023
|
+
} catch {
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
saveTransferPayload(transferId, payload).catch((err) => {
|
|
1027
|
+
console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
|
|
1028
|
+
});
|
|
1029
|
+
let targetUrl = null;
|
|
1030
|
+
if (this.currentOptions.standaloneViewerUrl) {
|
|
1031
|
+
const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
|
|
1032
|
+
u.searchParams.set("mode", "fullscreen");
|
|
1033
|
+
u.searchParams.set("transferId", transferId);
|
|
1034
|
+
targetUrl = u.toString();
|
|
1035
|
+
} else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
|
|
1036
|
+
const u = new URL(window.location.href);
|
|
1037
|
+
u.searchParams.set("mode", "fullscreen");
|
|
1038
|
+
u.searchParams.set("transferId", transferId);
|
|
1039
|
+
targetUrl = u.toString();
|
|
1040
|
+
}
|
|
1041
|
+
if (targetUrl) {
|
|
1042
|
+
const newWin2 = window.open(targetUrl, "_blank");
|
|
1043
|
+
if (!newWin2) {
|
|
1044
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
1045
|
+
return null;
|
|
1046
|
+
}
|
|
1047
|
+
return newWin2;
|
|
1048
|
+
}
|
|
1049
|
+
const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
|
|
1050
|
+
const newWin = window.open("", "_blank");
|
|
1051
|
+
if (!newWin) {
|
|
1052
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
1053
|
+
return null;
|
|
1054
|
+
}
|
|
1055
|
+
newWin.document.title = title;
|
|
1056
|
+
newWin.document.body.style.margin = "0";
|
|
1057
|
+
newWin.document.body.style.padding = "0";
|
|
1058
|
+
newWin.document.body.style.width = "100vw";
|
|
1059
|
+
newWin.document.body.style.height = "100vh";
|
|
1060
|
+
newWin.document.body.style.overflow = "hidden";
|
|
1061
|
+
newWin.document.body.style.backgroundColor = "#f8fafc";
|
|
1062
|
+
const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
|
|
1063
|
+
headNodes.forEach((node) => {
|
|
1064
|
+
newWin.document.head.appendChild(node.cloneNode(true));
|
|
1065
|
+
});
|
|
1066
|
+
const root = newWin.document.createElement("div");
|
|
1067
|
+
root.id = "full-window-preview-root";
|
|
1068
|
+
root.style.width = "100%";
|
|
1069
|
+
root.style.height = "100%";
|
|
1070
|
+
root.style.overflow = "hidden";
|
|
1071
|
+
newWin.document.body.appendChild(root);
|
|
1072
|
+
const separateViewer = new _FilePreviewViewer();
|
|
1073
|
+
for (const plugin of this.plugins) {
|
|
1074
|
+
separateViewer.registerPlugin(plugin);
|
|
1075
|
+
}
|
|
1076
|
+
separateViewer.preview(root, this.currentBuffer.slice(0), {
|
|
1077
|
+
...this.currentOptions,
|
|
1078
|
+
showToolbar: true,
|
|
1079
|
+
toolbarPosition: "top",
|
|
1080
|
+
metadata: this.currentMetadata || void 0,
|
|
1081
|
+
_isSeparateWindow: true
|
|
1082
|
+
}).catch((err) => {
|
|
1083
|
+
console.error("[FilePreviewViewer] Error rendering in separate window:", err);
|
|
1084
|
+
});
|
|
1085
|
+
newWin.addEventListener("beforeunload", () => {
|
|
1086
|
+
separateViewer.destroy();
|
|
1087
|
+
});
|
|
1088
|
+
return newWin;
|
|
1089
|
+
}
|
|
887
1090
|
/**
|
|
888
1091
|
* Subscribe to viewer events.
|
|
889
1092
|
*/
|
|
@@ -1289,8 +1492,20 @@ var CfbfReader = class {
|
|
|
1289
1492
|
}
|
|
1290
1493
|
};
|
|
1291
1494
|
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1292
|
-
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1293
|
-
|
|
1495
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerPort && !pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1496
|
+
const customWorker = window.__PDF_WORKER_SRC__;
|
|
1497
|
+
if (customWorker) {
|
|
1498
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = customWorker;
|
|
1499
|
+
} else {
|
|
1500
|
+
try {
|
|
1501
|
+
pdfjsLib.GlobalWorkerOptions.workerPort = new Worker(
|
|
1502
|
+
new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url),
|
|
1503
|
+
{ type: "module" }
|
|
1504
|
+
);
|
|
1505
|
+
} catch {
|
|
1506
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1294
1509
|
}
|
|
1295
1510
|
}
|
|
1296
1511
|
var PdfPlugin = class {
|
|
@@ -1384,6 +1599,14 @@ var PdfPlugin = class {
|
|
|
1384
1599
|
type: "button",
|
|
1385
1600
|
group: "actions",
|
|
1386
1601
|
execute: () => instance.print?.()
|
|
1602
|
+
},
|
|
1603
|
+
{
|
|
1604
|
+
id: "open-window",
|
|
1605
|
+
icon: "open-window",
|
|
1606
|
+
label: "Open in Separate Full Window",
|
|
1607
|
+
type: "button",
|
|
1608
|
+
group: "actions",
|
|
1609
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
1387
1610
|
}
|
|
1388
1611
|
];
|
|
1389
1612
|
}
|
|
@@ -1433,18 +1656,21 @@ var PdfPlugin = class {
|
|
|
1433
1656
|
indicator.style.pointerEvents = "none";
|
|
1434
1657
|
container.appendChild(indicator);
|
|
1435
1658
|
ctx.container.appendChild(container);
|
|
1659
|
+
const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
|
|
1660
|
+
const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
|
|
1436
1661
|
const loadingTask = pdfjsLib.getDocument({
|
|
1437
|
-
data: new Uint8Array(ctx.buffer),
|
|
1438
|
-
cMapUrl:
|
|
1662
|
+
data: new Uint8Array(ctx.buffer.slice(0)),
|
|
1663
|
+
cMapUrl: cmapsUrl,
|
|
1439
1664
|
cMapPacked: true,
|
|
1440
|
-
standardFontDataUrl:
|
|
1665
|
+
standardFontDataUrl: standardFontsUrl,
|
|
1666
|
+
verbosity: 0
|
|
1441
1667
|
});
|
|
1442
1668
|
const pdfDoc = await loadingTask.promise;
|
|
1443
1669
|
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1444
1670
|
let currentPage = 1;
|
|
1445
1671
|
let zoomScale = 1;
|
|
1446
1672
|
let rotation = 0;
|
|
1447
|
-
let fitMode = "
|
|
1673
|
+
let fitMode = "page";
|
|
1448
1674
|
let currentRenderTask = null;
|
|
1449
1675
|
const renderPage = async (pageNum) => {
|
|
1450
1676
|
if (currentRenderTask) {
|
|
@@ -1464,15 +1690,15 @@ var PdfPlugin = class {
|
|
|
1464
1690
|
const containerWidth = container.clientWidth || 900;
|
|
1465
1691
|
const containerHeight = container.clientHeight || 700;
|
|
1466
1692
|
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1467
|
-
const availWidth = Math.max(
|
|
1468
|
-
const availHeight = Math.max(
|
|
1693
|
+
const availWidth = Math.max(320, containerWidth - 48);
|
|
1694
|
+
const availHeight = Math.max(550, containerHeight - 88);
|
|
1469
1695
|
const scaleW = availWidth / unscaledVp.width;
|
|
1470
1696
|
const scaleH = availHeight / unscaledVp.height;
|
|
1471
1697
|
let fitScale;
|
|
1472
1698
|
if (fitMode === "page") {
|
|
1473
|
-
fitScale = Math.max(0.
|
|
1699
|
+
fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
|
|
1474
1700
|
} else {
|
|
1475
|
-
fitScale = Math.max(0.65, Math.min(1.
|
|
1701
|
+
fitScale = Math.max(0.65, Math.min(1.25, scaleW));
|
|
1476
1702
|
}
|
|
1477
1703
|
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1478
1704
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
@@ -1498,7 +1724,7 @@ var PdfPlugin = class {
|
|
|
1498
1724
|
currentRenderTask = null;
|
|
1499
1725
|
}
|
|
1500
1726
|
};
|
|
1501
|
-
|
|
1727
|
+
renderPage(1);
|
|
1502
1728
|
let resizeTimer = null;
|
|
1503
1729
|
const resizeObserver = new ResizeObserver(() => {
|
|
1504
1730
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -1542,7 +1768,7 @@ var PdfPlugin = class {
|
|
|
1542
1768
|
renderPage(currentPage);
|
|
1543
1769
|
},
|
|
1544
1770
|
fitToPage: () => {
|
|
1545
|
-
fitMode = fitMode === "
|
|
1771
|
+
fitMode = fitMode === "page" ? "width" : "page";
|
|
1546
1772
|
zoomScale = 1;
|
|
1547
1773
|
rotation = 0;
|
|
1548
1774
|
renderPage(currentPage);
|
|
@@ -1559,6 +1785,7 @@ var PdfPlugin = class {
|
|
|
1559
1785
|
getPageCount: () => totalPages,
|
|
1560
1786
|
getCurrentPage: () => currentPage,
|
|
1561
1787
|
goToPage: (page) => {
|
|
1788
|
+
container.scrollTop = 0;
|
|
1562
1789
|
renderPage(page);
|
|
1563
1790
|
},
|
|
1564
1791
|
download: () => {
|
|
@@ -2018,6 +2245,14 @@ var DocxPlugin = class {
|
|
|
2018
2245
|
type: "button",
|
|
2019
2246
|
group: "actions",
|
|
2020
2247
|
execute: () => instance.print?.()
|
|
2248
|
+
},
|
|
2249
|
+
{
|
|
2250
|
+
id: "open-window",
|
|
2251
|
+
icon: "open-window",
|
|
2252
|
+
label: "Open in Separate Full Window",
|
|
2253
|
+
type: "button",
|
|
2254
|
+
group: "actions",
|
|
2255
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
2021
2256
|
}
|
|
2022
2257
|
);
|
|
2023
2258
|
return actions;
|
|
@@ -2063,13 +2298,45 @@ var DocxPlugin = class {
|
|
|
2063
2298
|
ignoreFonts: true,
|
|
2064
2299
|
// Avoid crashes on embedded obfuscated fonts
|
|
2065
2300
|
breakPages: true,
|
|
2066
|
-
experimental: true
|
|
2301
|
+
experimental: true,
|
|
2302
|
+
ignoreLastRenderedPageBreak: false,
|
|
2303
|
+
// Honor Word's exact page breaks!
|
|
2304
|
+
renderHeaders: true,
|
|
2305
|
+
renderFooters: true,
|
|
2306
|
+
renderFootnotes: true,
|
|
2307
|
+
renderEndnotes: true,
|
|
2308
|
+
useBase64URL: true
|
|
2067
2309
|
});
|
|
2068
2310
|
if (!ctx.container.contains(wrapper)) {
|
|
2069
2311
|
ctx.container.appendChild(wrapper);
|
|
2070
2312
|
}
|
|
2071
2313
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
2072
2314
|
renderedSuccessfully = true;
|
|
2315
|
+
try {
|
|
2316
|
+
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
2317
|
+
const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
|
|
2318
|
+
if (chartKeys.length > 0) {
|
|
2319
|
+
const allDivs = Array.from(wrapper.querySelectorAll("div"));
|
|
2320
|
+
const emptyContainers = allDivs.filter((div) => {
|
|
2321
|
+
const st = div.getAttribute("style") || "";
|
|
2322
|
+
return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
|
|
2323
|
+
});
|
|
2324
|
+
chartKeys.forEach((cKey, idx) => {
|
|
2325
|
+
const target = emptyContainers[idx];
|
|
2326
|
+
if (target) {
|
|
2327
|
+
const xmlStr = strFromU8(unzipped[cKey]);
|
|
2328
|
+
const svg = this.parseAndRenderChartSvg(xmlStr);
|
|
2329
|
+
if (svg) {
|
|
2330
|
+
target.innerHTML = svg;
|
|
2331
|
+
target.style.display = "block";
|
|
2332
|
+
target.style.margin = "12px auto";
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
} catch (chartErr) {
|
|
2338
|
+
console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
|
|
2339
|
+
}
|
|
2073
2340
|
}
|
|
2074
2341
|
} catch (err) {
|
|
2075
2342
|
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
@@ -2084,11 +2351,14 @@ var DocxPlugin = class {
|
|
|
2084
2351
|
renderedSuccessfully = true;
|
|
2085
2352
|
} catch (fallbackErr) {
|
|
2086
2353
|
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
2354
|
+
const isCorrupt = fallbackErr?.message?.includes("invalid zip") || fallbackErr?.message?.includes("corrupted");
|
|
2087
2355
|
wrapper.innerHTML = `
|
|
2088
|
-
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
2089
|
-
<div style="font-size:48px; margin-bottom: 16px;"
|
|
2090
|
-
<h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2091
|
-
<p style="color: #64748b; margin: 0;
|
|
2356
|
+
<div style="text-align:center; padding: 48px 32px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06); max-width: 600px; margin: 40px auto;">
|
|
2357
|
+
<div style="font-size:48px; margin-bottom: 16px;">${isCorrupt ? "\u26A0\uFE0F" : "\u{1F4C4}"}</div>
|
|
2358
|
+
<h3 style="margin: 0 0 8px; color: #1e293b; font-size: 18px;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2359
|
+
<p style="color: #64748b; margin: 0 0 16px; font-size: 14px; line-height: 1.5;">
|
|
2360
|
+
${isCorrupt ? "This document appears to be corrupted or contains invalid archive data and cannot be opened (matches Microsoft Word on Windows)." : "Could not render document content. The file structure may be damaged."}
|
|
2361
|
+
</p>
|
|
2092
2362
|
</div>
|
|
2093
2363
|
`;
|
|
2094
2364
|
if (!ctx.container.contains(wrapper)) {
|
|
@@ -2098,50 +2368,74 @@ var DocxPlugin = class {
|
|
|
2098
2368
|
}
|
|
2099
2369
|
let sections = Array.from(wrapper.querySelectorAll("section.docx"));
|
|
2100
2370
|
const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
|
|
2101
|
-
if (sections.length
|
|
2102
|
-
const
|
|
2103
|
-
const
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
const
|
|
2107
|
-
|
|
2371
|
+
if (sections.length > 0 && cards.length === 0) {
|
|
2372
|
+
const finalSections = [];
|
|
2373
|
+
for (const singleSec of sections) {
|
|
2374
|
+
const contentContainer = singleSec.querySelector("article") || singleSec;
|
|
2375
|
+
const children = Array.from(contentContainer.children);
|
|
2376
|
+
const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
|
|
2377
|
+
const secH = singleSec.scrollHeight || singleSec.offsetHeight;
|
|
2378
|
+
if (secH > pageH * 1.25 && children.length > 1) {
|
|
2379
|
+
const childHeights = children.map((c) => {
|
|
2380
|
+
const rectH = c.getBoundingClientRect().height;
|
|
2381
|
+
const offH = c.offsetHeight;
|
|
2382
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
2383
|
+
const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
|
|
2384
|
+
return Math.max(rectH, offH, estH);
|
|
2385
|
+
});
|
|
2108
2386
|
const parent = singleSec.parentElement || wrapper;
|
|
2109
|
-
const
|
|
2110
|
-
|
|
2387
|
+
const headerEl = singleSec.querySelector("header");
|
|
2388
|
+
const footerEl = singleSec.querySelector("footer");
|
|
2389
|
+
contentContainer.innerHTML = "";
|
|
2111
2390
|
singleSec.style.minHeight = `${pageH}px`;
|
|
2112
|
-
singleSec.style.maxHeight = `${pageH}px`;
|
|
2113
|
-
singleSec.style.overflow = "hidden";
|
|
2114
2391
|
singleSec.style.boxSizing = "border-box";
|
|
2392
|
+
let curContent = contentContainer;
|
|
2115
2393
|
let curSec = singleSec;
|
|
2116
2394
|
let curH = 0;
|
|
2117
|
-
const maxH = pageH -
|
|
2395
|
+
const maxH = pageH - 140;
|
|
2396
|
+
finalSections.push(singleSec);
|
|
2118
2397
|
for (let i = 0; i < children.length; i++) {
|
|
2119
2398
|
const child = children[i];
|
|
2120
|
-
|
|
2121
|
-
|
|
2399
|
+
const chH = childHeights[i];
|
|
2400
|
+
curContent.appendChild(child);
|
|
2122
2401
|
curH += chH;
|
|
2123
2402
|
if (curH >= maxH && i < children.length - 1) {
|
|
2124
2403
|
const nextSec = document.createElement("section");
|
|
2125
2404
|
nextSec.className = singleSec.className;
|
|
2126
2405
|
nextSec.style.cssText = singleSec.style.cssText;
|
|
2127
|
-
nextSec.style.width = singleSec.style.width || "816px";
|
|
2128
2406
|
nextSec.style.minHeight = `${pageH}px`;
|
|
2129
|
-
nextSec.style.maxHeight = `${pageH}px`;
|
|
2130
|
-
nextSec.style.overflow = "hidden";
|
|
2131
2407
|
nextSec.style.boxSizing = "border-box";
|
|
2132
2408
|
nextSec.style.backgroundColor = "#ffffff";
|
|
2133
2409
|
nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
2134
2410
|
nextSec.style.borderRadius = "4px";
|
|
2135
2411
|
nextSec.style.marginBottom = "24px";
|
|
2136
|
-
|
|
2137
|
-
|
|
2412
|
+
if (headerEl) {
|
|
2413
|
+
nextSec.appendChild(headerEl.cloneNode(true));
|
|
2414
|
+
}
|
|
2415
|
+
const nextArticle = document.createElement("article");
|
|
2416
|
+
if (contentContainer.tagName.toLowerCase() === "article") {
|
|
2417
|
+
nextArticle.style.cssText = contentContainer.style.cssText;
|
|
2418
|
+
}
|
|
2419
|
+
nextSec.appendChild(nextArticle);
|
|
2420
|
+
if (footerEl) {
|
|
2421
|
+
nextSec.appendChild(footerEl.cloneNode(true));
|
|
2422
|
+
}
|
|
2423
|
+
if (curSec.nextSibling) {
|
|
2424
|
+
parent.insertBefore(nextSec, curSec.nextSibling);
|
|
2425
|
+
} else {
|
|
2426
|
+
parent.appendChild(nextSec);
|
|
2427
|
+
}
|
|
2428
|
+
finalSections.push(nextSec);
|
|
2138
2429
|
curSec = nextSec;
|
|
2430
|
+
curContent = nextArticle;
|
|
2139
2431
|
curH = 0;
|
|
2140
2432
|
}
|
|
2141
2433
|
}
|
|
2142
|
-
|
|
2434
|
+
} else {
|
|
2435
|
+
finalSections.push(singleSec);
|
|
2143
2436
|
}
|
|
2144
2437
|
}
|
|
2438
|
+
sections = finalSections;
|
|
2145
2439
|
}
|
|
2146
2440
|
const pageElements = sections.length > 0 ? sections : cards;
|
|
2147
2441
|
const totalPages = Math.max(1, pageElements.length);
|
|
@@ -2179,6 +2473,7 @@ var DocxPlugin = class {
|
|
|
2179
2473
|
if (indicator) {
|
|
2180
2474
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2181
2475
|
}
|
|
2476
|
+
ctx.container.scrollTop = 0;
|
|
2182
2477
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2183
2478
|
};
|
|
2184
2479
|
if (totalPages > 1) {
|
|
@@ -2532,6 +2827,88 @@ var DocxPlugin = class {
|
|
|
2532
2827
|
}
|
|
2533
2828
|
return result;
|
|
2534
2829
|
}
|
|
2830
|
+
parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
|
|
2831
|
+
const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
|
|
2832
|
+
let categories = [];
|
|
2833
|
+
if (catMatches.length > 0) {
|
|
2834
|
+
categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
|
|
2835
|
+
}
|
|
2836
|
+
if (categories.length === 0) {
|
|
2837
|
+
categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
|
|
2838
|
+
}
|
|
2839
|
+
const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
|
|
2840
|
+
const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
|
|
2841
|
+
const series = [];
|
|
2842
|
+
sers.forEach((s, sIdx) => {
|
|
2843
|
+
const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
|
|
2844
|
+
const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
|
|
2845
|
+
const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
|
|
2846
|
+
const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
|
|
2847
|
+
const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
|
|
2848
|
+
let values = [];
|
|
2849
|
+
if (valMatch) {
|
|
2850
|
+
values = [...valMatch[1].matchAll(/<c:pt\s+idx="(\d+)">\s*<c:v>([^<]+)<\/c:v>/g)].sort((a, b) => parseInt(a[1], 10) - parseInt(b[1], 10)).map((m) => parseFloat(m[2]) || 0);
|
|
2851
|
+
}
|
|
2852
|
+
series.push({ title, color, values });
|
|
2853
|
+
});
|
|
2854
|
+
if (series.length === 0) return "";
|
|
2855
|
+
let maxVal = 10;
|
|
2856
|
+
series.forEach((s) => s.values.forEach((v) => {
|
|
2857
|
+
if (v > maxVal) maxVal = v;
|
|
2858
|
+
}));
|
|
2859
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
2860
|
+
if (maxVal % 2 !== 0) maxVal++;
|
|
2861
|
+
const padLeft = 45;
|
|
2862
|
+
const padBottom = 55;
|
|
2863
|
+
const padTop = 20;
|
|
2864
|
+
const padRight = 20;
|
|
2865
|
+
const plotW = width - padLeft - padRight;
|
|
2866
|
+
const plotH = height - padTop - padBottom;
|
|
2867
|
+
const yTicks = 5;
|
|
2868
|
+
let gridLines = "";
|
|
2869
|
+
for (let i = 0; i <= yTicks; i++) {
|
|
2870
|
+
const val = maxVal / yTicks * i;
|
|
2871
|
+
const y = padTop + plotH - val / maxVal * plotH;
|
|
2872
|
+
gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
|
|
2873
|
+
gridLines += `<text x="${padLeft - 8}" y="${y + 4}" text-anchor="end" font-size="11" fill="#64748b" font-family="Calibri, sans-serif">${Math.round(val)}</text>`;
|
|
2874
|
+
}
|
|
2875
|
+
const numCats = categories.length;
|
|
2876
|
+
const numSers = series.length;
|
|
2877
|
+
const groupW = plotW / numCats;
|
|
2878
|
+
const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
|
|
2879
|
+
const groupPad = (groupW - barW * numSers) / 2;
|
|
2880
|
+
let bars = "";
|
|
2881
|
+
let catLabels = "";
|
|
2882
|
+
for (let c = 0; c < numCats; c++) {
|
|
2883
|
+
const catX = padLeft + c * groupW;
|
|
2884
|
+
catLabels += `<text x="${catX + groupW / 2}" y="${padTop + plotH + 18}" text-anchor="middle" font-size="11" fill="#334155" font-family="Calibri, sans-serif">${categories[c]}</text>`;
|
|
2885
|
+
for (let s = 0; s < numSers; s++) {
|
|
2886
|
+
const val = series[s].values[c] ?? 0;
|
|
2887
|
+
const bH = Math.max(0, val / maxVal * plotH);
|
|
2888
|
+
const bX = catX + groupPad + s * barW;
|
|
2889
|
+
const bY = padTop + plotH - bH;
|
|
2890
|
+
bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
|
|
2891
|
+
}
|
|
2892
|
+
}
|
|
2893
|
+
let legend = "";
|
|
2894
|
+
const legY = height - 12;
|
|
2895
|
+
let legX = padLeft + (plotW - numSers * 100) / 2;
|
|
2896
|
+
series.forEach((s) => {
|
|
2897
|
+
legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
|
|
2898
|
+
legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
|
|
2899
|
+
legX += 95;
|
|
2900
|
+
});
|
|
2901
|
+
return `
|
|
2902
|
+
<svg viewBox="0 0 ${width} ${height}" width="100%" height="100%" style="background:#ffffff; border-radius:4px; overflow:visible;" xmlns="http://www.w3.org/2000/svg">
|
|
2903
|
+
${gridLines}
|
|
2904
|
+
<line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2905
|
+
<line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2906
|
+
${bars}
|
|
2907
|
+
${catLabels}
|
|
2908
|
+
${legend}
|
|
2909
|
+
</svg>
|
|
2910
|
+
`.trim();
|
|
2911
|
+
}
|
|
2535
2912
|
};
|
|
2536
2913
|
function docxPlugin() {
|
|
2537
2914
|
return new DocxPlugin();
|
|
@@ -3098,6 +3475,16 @@ var CodePlugin = class {
|
|
|
3098
3475
|
execute: () => {
|
|
3099
3476
|
instance.print?.();
|
|
3100
3477
|
}
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
id: "open-window",
|
|
3481
|
+
icon: "open-window",
|
|
3482
|
+
label: "Open in Separate Full Window",
|
|
3483
|
+
type: "button",
|
|
3484
|
+
group: "actions",
|
|
3485
|
+
execute: () => {
|
|
3486
|
+
instance.openInSeparateWindow?.();
|
|
3487
|
+
}
|
|
3101
3488
|
}
|
|
3102
3489
|
);
|
|
3103
3490
|
return actions;
|
|
@@ -3111,17 +3498,39 @@ var CodePlugin = class {
|
|
|
3111
3498
|
let rawPages = [];
|
|
3112
3499
|
if (isTxt) {
|
|
3113
3500
|
const explicitPages = fullText.split(/(?:\f|\x0C)/);
|
|
3501
|
+
const charsPerLine = 85;
|
|
3502
|
+
const maxVisualLines = 45;
|
|
3503
|
+
const charsPerPage = charsPerLine * maxVisualLines;
|
|
3114
3504
|
for (const ep of explicitPages) {
|
|
3115
3505
|
const lines = ep.split(/\r?\n/);
|
|
3116
3506
|
let currentChunk = [];
|
|
3507
|
+
let currentLines = 0;
|
|
3117
3508
|
for (let i = 0; i < lines.length; i++) {
|
|
3118
|
-
|
|
3119
|
-
|
|
3509
|
+
const line = lines[i];
|
|
3510
|
+
const vLines = Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
|
|
3511
|
+
if (currentLines + vLines > maxVisualLines && currentChunk.length > 0) {
|
|
3120
3512
|
rawPages.push(currentChunk.join("\n"));
|
|
3121
3513
|
currentChunk = [];
|
|
3514
|
+
currentLines = 0;
|
|
3515
|
+
}
|
|
3516
|
+
if (vLines > maxVisualLines) {
|
|
3517
|
+
let remaining = line;
|
|
3518
|
+
while (remaining.length > charsPerPage) {
|
|
3519
|
+
let splitIdx = remaining.lastIndexOf(" ", charsPerPage);
|
|
3520
|
+
if (splitIdx < charsPerPage * 0.75) splitIdx = charsPerPage;
|
|
3521
|
+
rawPages.push(remaining.slice(0, splitIdx));
|
|
3522
|
+
remaining = remaining.slice(splitIdx).trimStart();
|
|
3523
|
+
}
|
|
3524
|
+
if (remaining.length > 0) {
|
|
3525
|
+
currentChunk.push(remaining);
|
|
3526
|
+
currentLines = Math.ceil(remaining.length / charsPerLine);
|
|
3527
|
+
}
|
|
3528
|
+
} else {
|
|
3529
|
+
currentChunk.push(line);
|
|
3530
|
+
currentLines += vLines;
|
|
3122
3531
|
}
|
|
3123
3532
|
}
|
|
3124
|
-
if (currentChunk.length > 0
|
|
3533
|
+
if (currentChunk.length > 0) {
|
|
3125
3534
|
rawPages.push(currentChunk.join("\n"));
|
|
3126
3535
|
}
|
|
3127
3536
|
}
|
|
@@ -3231,6 +3640,7 @@ var CodePlugin = class {
|
|
|
3231
3640
|
if (indicator) {
|
|
3232
3641
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3233
3642
|
}
|
|
3643
|
+
container.scrollTop = 0;
|
|
3234
3644
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3235
3645
|
};
|
|
3236
3646
|
if (totalPages > 1) {
|
|
@@ -4243,6 +4653,14 @@ var RtfPlugin = class {
|
|
|
4243
4653
|
type: "button",
|
|
4244
4654
|
group: "actions",
|
|
4245
4655
|
execute: () => instance.print?.()
|
|
4656
|
+
},
|
|
4657
|
+
{
|
|
4658
|
+
id: "open-window",
|
|
4659
|
+
icon: "open-window",
|
|
4660
|
+
label: "Open in Separate Full Window",
|
|
4661
|
+
type: "button",
|
|
4662
|
+
group: "actions",
|
|
4663
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4246
4664
|
}
|
|
4247
4665
|
);
|
|
4248
4666
|
return actions;
|
|
@@ -4295,11 +4713,55 @@ var RtfPlugin = class {
|
|
|
4295
4713
|
}
|
|
4296
4714
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
4297
4715
|
const htmlElements = await doc.render();
|
|
4298
|
-
|
|
4299
|
-
for (
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4716
|
+
const contentNodes = [];
|
|
4717
|
+
for (const item of htmlElements) {
|
|
4718
|
+
if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
|
|
4719
|
+
contentNodes.push(...Array.from(item.children));
|
|
4720
|
+
} else {
|
|
4721
|
+
contentNodes.push(item);
|
|
4722
|
+
}
|
|
4723
|
+
}
|
|
4724
|
+
wrapper.innerHTML = "";
|
|
4725
|
+
contentNodes.forEach((node) => wrapper.appendChild(node));
|
|
4726
|
+
const childHeights = contentNodes.map((c) => {
|
|
4727
|
+
const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
|
|
4728
|
+
const offH = c.offsetHeight || 0;
|
|
4729
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
4730
|
+
const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
|
|
4731
|
+
return Math.max(rectH, offH, estH);
|
|
4732
|
+
});
|
|
4733
|
+
wrapper.innerHTML = "";
|
|
4734
|
+
const createRtfCard = () => {
|
|
4735
|
+
const card = document.createElement("div");
|
|
4736
|
+
card.className = "fp-rtf-page-card";
|
|
4737
|
+
card.style.backgroundColor = "#ffffff";
|
|
4738
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
4739
|
+
card.style.borderRadius = "4px";
|
|
4740
|
+
card.style.padding = "72px 56px";
|
|
4741
|
+
card.style.width = "816px";
|
|
4742
|
+
card.style.minHeight = "1056px";
|
|
4743
|
+
card.style.boxSizing = "border-box";
|
|
4744
|
+
card.style.marginBottom = "24px";
|
|
4745
|
+
card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4746
|
+
card.style.lineHeight = "1.6";
|
|
4747
|
+
return card;
|
|
4748
|
+
};
|
|
4749
|
+
let curCard = createRtfCard();
|
|
4750
|
+
wrapper.appendChild(curCard);
|
|
4751
|
+
pageElements = [curCard];
|
|
4752
|
+
let curH = 0;
|
|
4753
|
+
const maxH = 912;
|
|
4754
|
+
for (let i = 0; i < contentNodes.length; i++) {
|
|
4755
|
+
const child = contentNodes[i];
|
|
4756
|
+
const chH = childHeights[i];
|
|
4757
|
+
curCard.appendChild(child);
|
|
4758
|
+
curH += chH;
|
|
4759
|
+
if (curH >= maxH && i < contentNodes.length - 1) {
|
|
4760
|
+
curCard = createRtfCard();
|
|
4761
|
+
wrapper.appendChild(curCard);
|
|
4762
|
+
pageElements.push(curCard);
|
|
4763
|
+
curH = 0;
|
|
4764
|
+
}
|
|
4303
4765
|
}
|
|
4304
4766
|
} catch (err) {
|
|
4305
4767
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
@@ -4365,6 +4827,7 @@ var RtfPlugin = class {
|
|
|
4365
4827
|
if (indicator) {
|
|
4366
4828
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4367
4829
|
}
|
|
4830
|
+
ctx.container.scrollTop = 0;
|
|
4368
4831
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4369
4832
|
};
|
|
4370
4833
|
if (totalPages > 1) {
|
|
@@ -4666,6 +5129,14 @@ var OpenDocumentPlugin = class {
|
|
|
4666
5129
|
type: "button",
|
|
4667
5130
|
group: "actions",
|
|
4668
5131
|
execute: () => instance.print?.()
|
|
5132
|
+
},
|
|
5133
|
+
{
|
|
5134
|
+
id: "open-window",
|
|
5135
|
+
icon: "open-window",
|
|
5136
|
+
label: "Open in Separate Full Window",
|
|
5137
|
+
type: "button",
|
|
5138
|
+
group: "actions",
|
|
5139
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4669
5140
|
}
|
|
4670
5141
|
);
|
|
4671
5142
|
return actions;
|
|
@@ -4836,12 +5307,9 @@ var OpenDocumentPlugin = class {
|
|
|
4836
5307
|
page.style.backgroundColor = "#ffffff";
|
|
4837
5308
|
page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
|
|
4838
5309
|
page.style.borderRadius = "4px";
|
|
4839
|
-
page.style.boxSizing = "border-box";
|
|
4840
5310
|
page.style.display = idx === 0 ? "block" : "none";
|
|
4841
|
-
page.style.
|
|
4842
|
-
page.style.
|
|
4843
|
-
page.style.left = "50%";
|
|
4844
|
-
page.style.transform = "translateX(-50%)";
|
|
5311
|
+
page.style.margin = "0 auto 24px";
|
|
5312
|
+
page.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
4845
5313
|
elements.forEach((el) => page.appendChild(el));
|
|
4846
5314
|
wrapper.appendChild(page);
|
|
4847
5315
|
slides.push(page);
|
|
@@ -4879,6 +5347,7 @@ var OpenDocumentPlugin = class {
|
|
|
4879
5347
|
if (indicator) {
|
|
4880
5348
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4881
5349
|
}
|
|
5350
|
+
container.scrollTop = 0;
|
|
4882
5351
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4883
5352
|
};
|
|
4884
5353
|
return {
|
|
@@ -5256,6 +5725,14 @@ var DocPlugin = class {
|
|
|
5256
5725
|
type: "button",
|
|
5257
5726
|
group: "actions",
|
|
5258
5727
|
execute: () => instance.print?.()
|
|
5728
|
+
},
|
|
5729
|
+
{
|
|
5730
|
+
id: "open-window",
|
|
5731
|
+
icon: "open-window",
|
|
5732
|
+
label: "Open in Separate Full Window",
|
|
5733
|
+
type: "button",
|
|
5734
|
+
group: "actions",
|
|
5735
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
5259
5736
|
}
|
|
5260
5737
|
);
|
|
5261
5738
|
return actions;
|
|
@@ -5371,6 +5848,7 @@ var DocPlugin = class {
|
|
|
5371
5848
|
if (indicator) {
|
|
5372
5849
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
5373
5850
|
}
|
|
5851
|
+
container.scrollTop = 0;
|
|
5374
5852
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
5375
5853
|
};
|
|
5376
5854
|
if (totalPages > 1) {
|
|
@@ -5546,47 +6024,69 @@ var DocPlugin = class {
|
|
|
5546
6024
|
heuristicTextExtraction(buffer) {
|
|
5547
6025
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5548
6026
|
}
|
|
6027
|
+
cleanWordDocFields(text) {
|
|
6028
|
+
if (!text) return "";
|
|
6029
|
+
let cleaned = text.replace(
|
|
6030
|
+
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
6031
|
+
(_match, url, label) => {
|
|
6032
|
+
const cleanUrl = url.trim();
|
|
6033
|
+
const cleanLabel = label.trim() || cleanUrl;
|
|
6034
|
+
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
6035
|
+
}
|
|
6036
|
+
);
|
|
6037
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, "$1");
|
|
6038
|
+
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6039
|
+
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6040
|
+
return cleaned;
|
|
6041
|
+
}
|
|
5549
6042
|
splitIntoPages(text) {
|
|
5550
6043
|
if (!text) return [""];
|
|
5551
|
-
const
|
|
5552
|
-
|
|
5553
|
-
const
|
|
6044
|
+
const cleanedText = this.cleanWordDocFields(text);
|
|
6045
|
+
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
6046
|
+
const explicitParts = normalized.split(/[\x0C\f]|\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
6047
|
+
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
6048
|
+
const maxLinesPerPage = 32;
|
|
6049
|
+
const charsPerLine = 80;
|
|
5554
6050
|
const finalPages = [];
|
|
5555
6051
|
for (const part of explicitParts) {
|
|
5556
|
-
const lines = part.split(
|
|
6052
|
+
const lines = part.split("\n");
|
|
5557
6053
|
let currentLines = [];
|
|
5558
6054
|
let count = 0;
|
|
5559
6055
|
for (const line of lines) {
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
if (count
|
|
6056
|
+
const plainLine = line.replace(/<[^>]+>/g, "");
|
|
6057
|
+
const vLines = Math.max(1, Math.ceil((plainLine.length || 1) / charsPerLine));
|
|
6058
|
+
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
5563
6059
|
finalPages.push(currentLines.join("\n"));
|
|
5564
6060
|
currentLines = [];
|
|
5565
6061
|
count = 0;
|
|
5566
6062
|
}
|
|
6063
|
+
currentLines.push(line);
|
|
6064
|
+
count += vLines;
|
|
5567
6065
|
}
|
|
5568
6066
|
if (currentLines.length > 0) {
|
|
5569
6067
|
finalPages.push(currentLines.join("\n"));
|
|
5570
6068
|
}
|
|
5571
6069
|
}
|
|
5572
|
-
return finalPages.length > 0 ? finalPages : [
|
|
6070
|
+
return finalPages.length > 0 ? finalPages : [normalized];
|
|
5573
6071
|
}
|
|
5574
6072
|
/**
|
|
5575
6073
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
5576
6074
|
*/
|
|
5577
6075
|
formatDocToHtml(text, filename) {
|
|
5578
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
6076
|
+
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ").split("\n");
|
|
5579
6077
|
let html = "";
|
|
5580
6078
|
let inList = false;
|
|
5581
6079
|
let tableLines = [];
|
|
5582
6080
|
const flushTable = () => {
|
|
5583
6081
|
if (tableLines.length > 0) {
|
|
5584
|
-
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size:
|
|
5585
|
-
for (
|
|
5586
|
-
|
|
5587
|
-
const
|
|
6082
|
+
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13px; font-family: Calibri, sans-serif;">';
|
|
6083
|
+
for (let rIdx = 0; rIdx < tableLines.length; rIdx++) {
|
|
6084
|
+
const tLine = tableLines[rIdx];
|
|
6085
|
+
const isHeader = rIdx === 0;
|
|
6086
|
+
html += `<tr style="${isHeader ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
6087
|
+
const cols = tLine.split(" ").filter((c) => c.trim().length > 0);
|
|
5588
6088
|
for (const col of cols) {
|
|
5589
|
-
html += `<td style="border: 1px solid #cbd5e1; padding:
|
|
6089
|
+
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px;">${DOMPurify6.sanitize(col.trim())}</td>`;
|
|
5590
6090
|
}
|
|
5591
6091
|
html += "</tr>";
|
|
5592
6092
|
}
|
|
@@ -5599,27 +6099,18 @@ var DocPlugin = class {
|
|
|
5599
6099
|
let line = lines[i];
|
|
5600
6100
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
5601
6101
|
if (tabCount > 0) {
|
|
5602
|
-
let
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
const nextTabCount = (lines[j].match(/\t/g) || []).length;
|
|
5606
|
-
if (nextTabCount === tabCount) {
|
|
5607
|
-
consecutiveTableLines++;
|
|
5608
|
-
j++;
|
|
5609
|
-
} else {
|
|
5610
|
-
break;
|
|
5611
|
-
}
|
|
6102
|
+
let j = i;
|
|
6103
|
+
while (j < lines.length && (lines[j].match(/\t/g) || []).length > 0) {
|
|
6104
|
+
j++;
|
|
5612
6105
|
}
|
|
5613
|
-
if (
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
inList = false;
|
|
5617
|
-
}
|
|
5618
|
-
tableLines = lines.slice(i, j);
|
|
5619
|
-
flushTable();
|
|
5620
|
-
i = j;
|
|
5621
|
-
continue;
|
|
6106
|
+
if (inList) {
|
|
6107
|
+
html += "</ul>";
|
|
6108
|
+
inList = false;
|
|
5622
6109
|
}
|
|
6110
|
+
tableLines = lines.slice(i, j);
|
|
6111
|
+
flushTable();
|
|
6112
|
+
i = j;
|
|
6113
|
+
continue;
|
|
5623
6114
|
}
|
|
5624
6115
|
line = line.trim();
|
|
5625
6116
|
if (!line) {
|
|
@@ -5639,25 +6130,26 @@ var DocPlugin = class {
|
|
|
5639
6130
|
i++;
|
|
5640
6131
|
continue;
|
|
5641
6132
|
}
|
|
6133
|
+
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
5642
6134
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
5643
6135
|
if (inList) {
|
|
5644
6136
|
html += "</ul>";
|
|
5645
6137
|
inList = false;
|
|
5646
6138
|
}
|
|
5647
|
-
html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line)}</h2>`;
|
|
6139
|
+
html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
|
|
5648
6140
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
5649
6141
|
if (!inList) {
|
|
5650
6142
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
5651
6143
|
inList = true;
|
|
5652
6144
|
}
|
|
5653
6145
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
5654
|
-
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
|
|
6146
|
+
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
|
|
5655
6147
|
} else {
|
|
5656
6148
|
if (inList) {
|
|
5657
6149
|
html += "</ul>";
|
|
5658
6150
|
inList = false;
|
|
5659
6151
|
}
|
|
5660
|
-
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
|
|
6152
|
+
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
|
|
5661
6153
|
}
|
|
5662
6154
|
i++;
|
|
5663
6155
|
}
|
|
@@ -6210,6 +6702,6 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
|
|
|
6210
6702
|
}
|
|
6211
6703
|
};
|
|
6212
6704
|
|
|
6213
|
-
export { CfbfReader, EventEmitter, FilePreviewViewer2 as FilePreviewViewer, ThumbnailPanel, ToolbarController, archivePlugin, clamp, codePlugin, createElement, csvPlugin, debounce, detectMagicBytes, detectOoxmlType, docPlugin, docxPlugin, downloadFile, excelPlugin, extractExtension, formatFileSize, getDefaultPlugins, htmlPreviewPlugin, markdownPlugin, mediaPlugin, mimeFromExtension, openDocumentPlugin, pdfPlugin, pptPlugin, pptxPlugin, printElement, rtfPlugin, sanitizeHTML, sanitizeSVG, sourceToArrayBuffer, threeDPlugin };
|
|
6705
|
+
export { CfbfReader, EventEmitter, FilePreviewViewer2 as FilePreviewViewer, ThumbnailPanel, ToolbarController, archivePlugin, clamp, codePlugin, createElement, csvPlugin, debounce, detectMagicBytes, detectOoxmlType, docPlugin, docxPlugin, downloadFile, excelPlugin, extractExtension, formatFileSize, getDefaultPlugins, getTransferPayload, htmlPreviewPlugin, markdownPlugin, mediaPlugin, mimeFromExtension, openDocumentPlugin, pdfPlugin, pptPlugin, pptxPlugin, printElement, rtfPlugin, sanitizeHTML, sanitizeSVG, saveTransferPayload, sourceToArrayBuffer, threeDPlugin };
|
|
6214
6706
|
//# sourceMappingURL=index.js.map
|
|
6215
6707
|
//# sourceMappingURL=index.js.map
|