@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/angular.js
CHANGED
|
@@ -385,16 +385,21 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
385
385
|
metadata.mimeType = source.type || void 0;
|
|
386
386
|
metadata.extension = extractExtension(source.name);
|
|
387
387
|
buffer = await source.arrayBuffer();
|
|
388
|
-
} else if (source instanceof Blob) {
|
|
388
|
+
} else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
|
|
389
389
|
metadata.size = source.size;
|
|
390
390
|
metadata.mimeType = source.type || void 0;
|
|
391
|
+
if (source.name) {
|
|
392
|
+
metadata.name = source.name;
|
|
393
|
+
metadata.extension = extractExtension(source.name);
|
|
394
|
+
}
|
|
391
395
|
buffer = await source.arrayBuffer();
|
|
392
|
-
} else if (source instanceof ArrayBuffer) {
|
|
396
|
+
} else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
|
|
393
397
|
buffer = source;
|
|
394
|
-
} else if (source instanceof Uint8Array) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
+
} else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
|
|
399
|
+
const view = source;
|
|
400
|
+
buffer = view.buffer.slice(
|
|
401
|
+
view.byteOffset,
|
|
402
|
+
view.byteOffset + view.byteLength
|
|
398
403
|
);
|
|
399
404
|
} else {
|
|
400
405
|
throw new Error("Unsupported file source type");
|
|
@@ -479,6 +484,45 @@ function createElement(tag, attrs, ...children) {
|
|
|
479
484
|
}
|
|
480
485
|
return el;
|
|
481
486
|
}
|
|
487
|
+
var DB_NAME = "PreviewFileTransferDB";
|
|
488
|
+
var DB_STORE = "transfers";
|
|
489
|
+
function openDB() {
|
|
490
|
+
return new Promise((resolve, reject) => {
|
|
491
|
+
if (typeof indexedDB === "undefined") {
|
|
492
|
+
return reject(new Error("IndexedDB is not available"));
|
|
493
|
+
}
|
|
494
|
+
const req = indexedDB.open(DB_NAME, 1);
|
|
495
|
+
req.onupgradeneeded = () => {
|
|
496
|
+
const db = req.result;
|
|
497
|
+
if (!db.objectStoreNames.contains(DB_STORE)) {
|
|
498
|
+
db.createObjectStore(DB_STORE, { keyPath: "id" });
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
req.onsuccess = () => resolve(req.result);
|
|
502
|
+
req.onerror = () => reject(req.error);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
async function saveTransferPayload(id, payload) {
|
|
506
|
+
if (typeof window !== "undefined") {
|
|
507
|
+
try {
|
|
508
|
+
window[id] = payload;
|
|
509
|
+
window.__lastTransfer = payload;
|
|
510
|
+
} catch {
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
const db = await openDB();
|
|
515
|
+
return new Promise((resolve, reject) => {
|
|
516
|
+
const tx = db.transaction(DB_STORE, "readwrite");
|
|
517
|
+
const store = tx.objectStore(DB_STORE);
|
|
518
|
+
store.put({ id, ...payload, timestamp: Date.now() });
|
|
519
|
+
tx.oncomplete = () => resolve();
|
|
520
|
+
tx.onerror = () => reject(tx.error);
|
|
521
|
+
});
|
|
522
|
+
} catch (e) {
|
|
523
|
+
console.warn("[saveTransferPayload] IndexedDB store warning:", e);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
482
526
|
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>`;
|
|
483
527
|
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>`;
|
|
484
528
|
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>`;
|
|
@@ -498,6 +542,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
|
|
|
498
542
|
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>`;
|
|
499
543
|
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>`;
|
|
500
544
|
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>`;
|
|
545
|
+
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>`;
|
|
501
546
|
var ICON_MAP = {
|
|
502
547
|
"zoom-in": ICON_ZOOM_IN,
|
|
503
548
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -524,7 +569,9 @@ var ICON_MAP = {
|
|
|
524
569
|
"forward-10": ICON_FAST_FORWARD,
|
|
525
570
|
"rewind": ICON_REWIND,
|
|
526
571
|
"replay-10": ICON_REWIND,
|
|
527
|
-
"speed": ICON_SPEED
|
|
572
|
+
"speed": ICON_SPEED,
|
|
573
|
+
"open-window": ICON_EXTERNAL_WINDOW,
|
|
574
|
+
"external-window": ICON_EXTERNAL_WINDOW
|
|
528
575
|
};
|
|
529
576
|
var ToolbarController = class {
|
|
530
577
|
el;
|
|
@@ -741,7 +788,7 @@ var ThumbnailPanel = class {
|
|
|
741
788
|
}
|
|
742
789
|
}
|
|
743
790
|
};
|
|
744
|
-
var FilePreviewViewer = class {
|
|
791
|
+
var FilePreviewViewer = class _FilePreviewViewer {
|
|
745
792
|
plugins = [];
|
|
746
793
|
activeInstance = null;
|
|
747
794
|
abortController = null;
|
|
@@ -778,6 +825,7 @@ var FilePreviewViewer = class {
|
|
|
778
825
|
* Preview a file in the given container element.
|
|
779
826
|
*/
|
|
780
827
|
async preview(container, source, options = {}) {
|
|
828
|
+
this.currentOptions = options;
|
|
781
829
|
this.abort();
|
|
782
830
|
this.abortController = new AbortController();
|
|
783
831
|
const { signal } = this.abortController;
|
|
@@ -787,7 +835,10 @@ var FilePreviewViewer = class {
|
|
|
787
835
|
this.showLoading();
|
|
788
836
|
try {
|
|
789
837
|
const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
|
|
790
|
-
|
|
838
|
+
if (options.metadata) {
|
|
839
|
+
Object.assign(metadata, options.metadata);
|
|
840
|
+
}
|
|
841
|
+
this.currentBuffer = buffer.slice(0);
|
|
791
842
|
this.currentMetadata = metadata;
|
|
792
843
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
793
844
|
const fileInfo = { metadata, buffer };
|
|
@@ -817,6 +868,7 @@ var FilePreviewViewer = class {
|
|
|
817
868
|
}
|
|
818
869
|
});
|
|
819
870
|
this.activeInstance = instance;
|
|
871
|
+
instance.openInSeparateWindow = () => this.openInSeparateWindow();
|
|
820
872
|
this.hideLoading();
|
|
821
873
|
this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
|
|
822
874
|
if (options.showToolbar !== false && this.toolbar) {
|
|
@@ -826,26 +878,16 @@ var FilePreviewViewer = class {
|
|
|
826
878
|
actions.push({
|
|
827
879
|
id: "fullscreen",
|
|
828
880
|
icon: "fullscreen",
|
|
829
|
-
label: "
|
|
881
|
+
label: "Fullscreen",
|
|
830
882
|
type: "button",
|
|
831
883
|
group: "view",
|
|
832
|
-
execute:
|
|
884
|
+
execute: () => {
|
|
833
885
|
try {
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
if (this.wrapperEl?.requestFullscreen) {
|
|
838
|
-
await this.wrapperEl.requestFullscreen().catch(() => {
|
|
839
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
840
|
-
});
|
|
841
|
-
} else {
|
|
842
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
843
|
-
}
|
|
886
|
+
if (!document.fullscreenElement) {
|
|
887
|
+
this.wrapperEl?.requestFullscreen?.();
|
|
888
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
844
889
|
} else {
|
|
845
|
-
|
|
846
|
-
await document.exitFullscreen().catch(() => {
|
|
847
|
-
});
|
|
848
|
-
}
|
|
890
|
+
document.exitFullscreen?.();
|
|
849
891
|
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
850
892
|
}
|
|
851
893
|
} catch {
|
|
@@ -857,6 +899,28 @@ var FilePreviewViewer = class {
|
|
|
857
899
|
}
|
|
858
900
|
});
|
|
859
901
|
}
|
|
902
|
+
const openWinAction = actions.find((a) => a.id === "open-window");
|
|
903
|
+
if (openWinAction) {
|
|
904
|
+
if (options?._isSeparateWindow) {
|
|
905
|
+
const idx = actions.indexOf(openWinAction);
|
|
906
|
+
if (idx !== -1) actions.splice(idx, 1);
|
|
907
|
+
} else {
|
|
908
|
+
openWinAction.execute = () => {
|
|
909
|
+
this.openInSeparateWindow();
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
} else if (!options?._isSeparateWindow) {
|
|
913
|
+
actions.push({
|
|
914
|
+
id: "open-window",
|
|
915
|
+
icon: "open-window",
|
|
916
|
+
label: "Open in Separate Full Window",
|
|
917
|
+
type: "button",
|
|
918
|
+
group: "actions",
|
|
919
|
+
execute: () => {
|
|
920
|
+
this.openInSeparateWindow();
|
|
921
|
+
}
|
|
922
|
+
});
|
|
923
|
+
}
|
|
860
924
|
this.toolbar.update(actions);
|
|
861
925
|
this.toolbar.show();
|
|
862
926
|
}
|
|
@@ -889,6 +953,104 @@ var FilePreviewViewer = class {
|
|
|
889
953
|
throw error;
|
|
890
954
|
}
|
|
891
955
|
}
|
|
956
|
+
/**
|
|
957
|
+
* Opens the current file preview in a separate full browser window.
|
|
958
|
+
*/
|
|
959
|
+
openInSeparateWindow() {
|
|
960
|
+
if (!this.currentBuffer) {
|
|
961
|
+
console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
|
|
962
|
+
return null;
|
|
963
|
+
}
|
|
964
|
+
if (this.currentOptions.onOpenSeparateWindow) {
|
|
965
|
+
return this.currentOptions.onOpenSeparateWindow({
|
|
966
|
+
buffer: this.currentBuffer,
|
|
967
|
+
metadata: this.currentMetadata || { name: "Document" },
|
|
968
|
+
options: this.currentOptions
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
|
|
972
|
+
let clonedBuffer;
|
|
973
|
+
try {
|
|
974
|
+
clonedBuffer = this.currentBuffer.slice(0);
|
|
975
|
+
} catch {
|
|
976
|
+
clonedBuffer = this.currentBuffer;
|
|
977
|
+
}
|
|
978
|
+
const payload = {
|
|
979
|
+
buffer: clonedBuffer,
|
|
980
|
+
metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
|
|
981
|
+
options: { ...this.currentOptions, _isSeparateWindow: true }
|
|
982
|
+
};
|
|
983
|
+
if (typeof window !== "undefined") {
|
|
984
|
+
try {
|
|
985
|
+
window[transferId] = payload;
|
|
986
|
+
window.__lastTransfer = payload;
|
|
987
|
+
} catch {
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
saveTransferPayload(transferId, payload).catch((err) => {
|
|
991
|
+
console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
|
|
992
|
+
});
|
|
993
|
+
let targetUrl = null;
|
|
994
|
+
if (this.currentOptions.standaloneViewerUrl) {
|
|
995
|
+
const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
|
|
996
|
+
u.searchParams.set("mode", "fullscreen");
|
|
997
|
+
u.searchParams.set("transferId", transferId);
|
|
998
|
+
targetUrl = u.toString();
|
|
999
|
+
} else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
|
|
1000
|
+
const u = new URL(window.location.href);
|
|
1001
|
+
u.searchParams.set("mode", "fullscreen");
|
|
1002
|
+
u.searchParams.set("transferId", transferId);
|
|
1003
|
+
targetUrl = u.toString();
|
|
1004
|
+
}
|
|
1005
|
+
if (targetUrl) {
|
|
1006
|
+
const newWin2 = window.open(targetUrl, "_blank");
|
|
1007
|
+
if (!newWin2) {
|
|
1008
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
1011
|
+
return newWin2;
|
|
1012
|
+
}
|
|
1013
|
+
const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
|
|
1014
|
+
const newWin = window.open("", "_blank");
|
|
1015
|
+
if (!newWin) {
|
|
1016
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
1017
|
+
return null;
|
|
1018
|
+
}
|
|
1019
|
+
newWin.document.title = title;
|
|
1020
|
+
newWin.document.body.style.margin = "0";
|
|
1021
|
+
newWin.document.body.style.padding = "0";
|
|
1022
|
+
newWin.document.body.style.width = "100vw";
|
|
1023
|
+
newWin.document.body.style.height = "100vh";
|
|
1024
|
+
newWin.document.body.style.overflow = "hidden";
|
|
1025
|
+
newWin.document.body.style.backgroundColor = "#f8fafc";
|
|
1026
|
+
const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
|
|
1027
|
+
headNodes.forEach((node) => {
|
|
1028
|
+
newWin.document.head.appendChild(node.cloneNode(true));
|
|
1029
|
+
});
|
|
1030
|
+
const root = newWin.document.createElement("div");
|
|
1031
|
+
root.id = "full-window-preview-root";
|
|
1032
|
+
root.style.width = "100%";
|
|
1033
|
+
root.style.height = "100%";
|
|
1034
|
+
root.style.overflow = "hidden";
|
|
1035
|
+
newWin.document.body.appendChild(root);
|
|
1036
|
+
const separateViewer = new _FilePreviewViewer();
|
|
1037
|
+
for (const plugin of this.plugins) {
|
|
1038
|
+
separateViewer.registerPlugin(plugin);
|
|
1039
|
+
}
|
|
1040
|
+
separateViewer.preview(root, this.currentBuffer.slice(0), {
|
|
1041
|
+
...this.currentOptions,
|
|
1042
|
+
showToolbar: true,
|
|
1043
|
+
toolbarPosition: "top",
|
|
1044
|
+
metadata: this.currentMetadata || void 0,
|
|
1045
|
+
_isSeparateWindow: true
|
|
1046
|
+
}).catch((err) => {
|
|
1047
|
+
console.error("[FilePreviewViewer] Error rendering in separate window:", err);
|
|
1048
|
+
});
|
|
1049
|
+
newWin.addEventListener("beforeunload", () => {
|
|
1050
|
+
separateViewer.destroy();
|
|
1051
|
+
});
|
|
1052
|
+
return newWin;
|
|
1053
|
+
}
|
|
892
1054
|
/**
|
|
893
1055
|
* Subscribe to viewer events.
|
|
894
1056
|
*/
|
|
@@ -1294,8 +1456,20 @@ var CfbfReader = class {
|
|
|
1294
1456
|
}
|
|
1295
1457
|
};
|
|
1296
1458
|
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1297
|
-
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1298
|
-
|
|
1459
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerPort && !pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1460
|
+
const customWorker = window.__PDF_WORKER_SRC__;
|
|
1461
|
+
if (customWorker) {
|
|
1462
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = customWorker;
|
|
1463
|
+
} else {
|
|
1464
|
+
try {
|
|
1465
|
+
pdfjsLib.GlobalWorkerOptions.workerPort = new Worker(
|
|
1466
|
+
new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url),
|
|
1467
|
+
{ type: "module" }
|
|
1468
|
+
);
|
|
1469
|
+
} catch {
|
|
1470
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1299
1473
|
}
|
|
1300
1474
|
}
|
|
1301
1475
|
var PdfPlugin = class {
|
|
@@ -1389,6 +1563,14 @@ var PdfPlugin = class {
|
|
|
1389
1563
|
type: "button",
|
|
1390
1564
|
group: "actions",
|
|
1391
1565
|
execute: () => instance.print?.()
|
|
1566
|
+
},
|
|
1567
|
+
{
|
|
1568
|
+
id: "open-window",
|
|
1569
|
+
icon: "open-window",
|
|
1570
|
+
label: "Open in Separate Full Window",
|
|
1571
|
+
type: "button",
|
|
1572
|
+
group: "actions",
|
|
1573
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
1392
1574
|
}
|
|
1393
1575
|
];
|
|
1394
1576
|
}
|
|
@@ -1438,18 +1620,21 @@ var PdfPlugin = class {
|
|
|
1438
1620
|
indicator.style.pointerEvents = "none";
|
|
1439
1621
|
container.appendChild(indicator);
|
|
1440
1622
|
ctx.container.appendChild(container);
|
|
1623
|
+
const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
|
|
1624
|
+
const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
|
|
1441
1625
|
const loadingTask = pdfjsLib.getDocument({
|
|
1442
|
-
data: new Uint8Array(ctx.buffer),
|
|
1443
|
-
cMapUrl:
|
|
1626
|
+
data: new Uint8Array(ctx.buffer.slice(0)),
|
|
1627
|
+
cMapUrl: cmapsUrl,
|
|
1444
1628
|
cMapPacked: true,
|
|
1445
|
-
standardFontDataUrl:
|
|
1629
|
+
standardFontDataUrl: standardFontsUrl,
|
|
1630
|
+
verbosity: 0
|
|
1446
1631
|
});
|
|
1447
1632
|
const pdfDoc = await loadingTask.promise;
|
|
1448
1633
|
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1449
1634
|
let currentPage = 1;
|
|
1450
1635
|
let zoomScale = 1;
|
|
1451
1636
|
let rotation = 0;
|
|
1452
|
-
let fitMode = "
|
|
1637
|
+
let fitMode = "page";
|
|
1453
1638
|
let currentRenderTask = null;
|
|
1454
1639
|
const renderPage = async (pageNum) => {
|
|
1455
1640
|
if (currentRenderTask) {
|
|
@@ -1469,15 +1654,15 @@ var PdfPlugin = class {
|
|
|
1469
1654
|
const containerWidth = container.clientWidth || 900;
|
|
1470
1655
|
const containerHeight = container.clientHeight || 700;
|
|
1471
1656
|
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1472
|
-
const availWidth = Math.max(
|
|
1473
|
-
const availHeight = Math.max(
|
|
1657
|
+
const availWidth = Math.max(320, containerWidth - 48);
|
|
1658
|
+
const availHeight = Math.max(550, containerHeight - 88);
|
|
1474
1659
|
const scaleW = availWidth / unscaledVp.width;
|
|
1475
1660
|
const scaleH = availHeight / unscaledVp.height;
|
|
1476
1661
|
let fitScale;
|
|
1477
1662
|
if (fitMode === "page") {
|
|
1478
|
-
fitScale = Math.max(0.
|
|
1663
|
+
fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
|
|
1479
1664
|
} else {
|
|
1480
|
-
fitScale = Math.max(0.65, Math.min(1.
|
|
1665
|
+
fitScale = Math.max(0.65, Math.min(1.25, scaleW));
|
|
1481
1666
|
}
|
|
1482
1667
|
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1483
1668
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
@@ -1503,7 +1688,7 @@ var PdfPlugin = class {
|
|
|
1503
1688
|
currentRenderTask = null;
|
|
1504
1689
|
}
|
|
1505
1690
|
};
|
|
1506
|
-
|
|
1691
|
+
renderPage(1);
|
|
1507
1692
|
let resizeTimer = null;
|
|
1508
1693
|
const resizeObserver = new ResizeObserver(() => {
|
|
1509
1694
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -1547,7 +1732,7 @@ var PdfPlugin = class {
|
|
|
1547
1732
|
renderPage(currentPage);
|
|
1548
1733
|
},
|
|
1549
1734
|
fitToPage: () => {
|
|
1550
|
-
fitMode = fitMode === "
|
|
1735
|
+
fitMode = fitMode === "page" ? "width" : "page";
|
|
1551
1736
|
zoomScale = 1;
|
|
1552
1737
|
rotation = 0;
|
|
1553
1738
|
renderPage(currentPage);
|
|
@@ -1564,6 +1749,7 @@ var PdfPlugin = class {
|
|
|
1564
1749
|
getPageCount: () => totalPages,
|
|
1565
1750
|
getCurrentPage: () => currentPage,
|
|
1566
1751
|
goToPage: (page) => {
|
|
1752
|
+
container.scrollTop = 0;
|
|
1567
1753
|
renderPage(page);
|
|
1568
1754
|
},
|
|
1569
1755
|
download: () => {
|
|
@@ -2023,6 +2209,14 @@ var DocxPlugin = class {
|
|
|
2023
2209
|
type: "button",
|
|
2024
2210
|
group: "actions",
|
|
2025
2211
|
execute: () => instance.print?.()
|
|
2212
|
+
},
|
|
2213
|
+
{
|
|
2214
|
+
id: "open-window",
|
|
2215
|
+
icon: "open-window",
|
|
2216
|
+
label: "Open in Separate Full Window",
|
|
2217
|
+
type: "button",
|
|
2218
|
+
group: "actions",
|
|
2219
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
2026
2220
|
}
|
|
2027
2221
|
);
|
|
2028
2222
|
return actions;
|
|
@@ -2068,13 +2262,45 @@ var DocxPlugin = class {
|
|
|
2068
2262
|
ignoreFonts: true,
|
|
2069
2263
|
// Avoid crashes on embedded obfuscated fonts
|
|
2070
2264
|
breakPages: true,
|
|
2071
|
-
experimental: true
|
|
2265
|
+
experimental: true,
|
|
2266
|
+
ignoreLastRenderedPageBreak: false,
|
|
2267
|
+
// Honor Word's exact page breaks!
|
|
2268
|
+
renderHeaders: true,
|
|
2269
|
+
renderFooters: true,
|
|
2270
|
+
renderFootnotes: true,
|
|
2271
|
+
renderEndnotes: true,
|
|
2272
|
+
useBase64URL: true
|
|
2072
2273
|
});
|
|
2073
2274
|
if (!ctx.container.contains(wrapper)) {
|
|
2074
2275
|
ctx.container.appendChild(wrapper);
|
|
2075
2276
|
}
|
|
2076
2277
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
2077
2278
|
renderedSuccessfully = true;
|
|
2279
|
+
try {
|
|
2280
|
+
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
2281
|
+
const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
|
|
2282
|
+
if (chartKeys.length > 0) {
|
|
2283
|
+
const allDivs = Array.from(wrapper.querySelectorAll("div"));
|
|
2284
|
+
const emptyContainers = allDivs.filter((div) => {
|
|
2285
|
+
const st = div.getAttribute("style") || "";
|
|
2286
|
+
return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
|
|
2287
|
+
});
|
|
2288
|
+
chartKeys.forEach((cKey, idx) => {
|
|
2289
|
+
const target = emptyContainers[idx];
|
|
2290
|
+
if (target) {
|
|
2291
|
+
const xmlStr = strFromU8(unzipped[cKey]);
|
|
2292
|
+
const svg = this.parseAndRenderChartSvg(xmlStr);
|
|
2293
|
+
if (svg) {
|
|
2294
|
+
target.innerHTML = svg;
|
|
2295
|
+
target.style.display = "block";
|
|
2296
|
+
target.style.margin = "12px auto";
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
} catch (chartErr) {
|
|
2302
|
+
console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
|
|
2303
|
+
}
|
|
2078
2304
|
}
|
|
2079
2305
|
} catch (err) {
|
|
2080
2306
|
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
@@ -2089,11 +2315,14 @@ var DocxPlugin = class {
|
|
|
2089
2315
|
renderedSuccessfully = true;
|
|
2090
2316
|
} catch (fallbackErr) {
|
|
2091
2317
|
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
2318
|
+
const isCorrupt = fallbackErr?.message?.includes("invalid zip") || fallbackErr?.message?.includes("corrupted");
|
|
2092
2319
|
wrapper.innerHTML = `
|
|
2093
|
-
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
2094
|
-
<div style="font-size:48px; margin-bottom: 16px;"
|
|
2095
|
-
<h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2096
|
-
<p style="color: #64748b; margin: 0;
|
|
2320
|
+
<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;">
|
|
2321
|
+
<div style="font-size:48px; margin-bottom: 16px;">${isCorrupt ? "\u26A0\uFE0F" : "\u{1F4C4}"}</div>
|
|
2322
|
+
<h3 style="margin: 0 0 8px; color: #1e293b; font-size: 18px;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2323
|
+
<p style="color: #64748b; margin: 0 0 16px; font-size: 14px; line-height: 1.5;">
|
|
2324
|
+
${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."}
|
|
2325
|
+
</p>
|
|
2097
2326
|
</div>
|
|
2098
2327
|
`;
|
|
2099
2328
|
if (!ctx.container.contains(wrapper)) {
|
|
@@ -2103,50 +2332,74 @@ var DocxPlugin = class {
|
|
|
2103
2332
|
}
|
|
2104
2333
|
let sections = Array.from(wrapper.querySelectorAll("section.docx"));
|
|
2105
2334
|
const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
|
|
2106
|
-
if (sections.length
|
|
2107
|
-
const
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2335
|
+
if (sections.length > 0 && cards.length === 0) {
|
|
2336
|
+
const finalSections = [];
|
|
2337
|
+
for (const singleSec of sections) {
|
|
2338
|
+
const contentContainer = singleSec.querySelector("article") || singleSec;
|
|
2339
|
+
const children = Array.from(contentContainer.children);
|
|
2340
|
+
const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
|
|
2341
|
+
const secH = singleSec.scrollHeight || singleSec.offsetHeight;
|
|
2342
|
+
if (secH > pageH * 1.25 && children.length > 1) {
|
|
2343
|
+
const childHeights = children.map((c) => {
|
|
2344
|
+
const rectH = c.getBoundingClientRect().height;
|
|
2345
|
+
const offH = c.offsetHeight;
|
|
2346
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
2347
|
+
const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
|
|
2348
|
+
return Math.max(rectH, offH, estH);
|
|
2349
|
+
});
|
|
2113
2350
|
const parent = singleSec.parentElement || wrapper;
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2351
|
+
const headerEl = singleSec.querySelector("header");
|
|
2352
|
+
const footerEl = singleSec.querySelector("footer");
|
|
2353
|
+
contentContainer.innerHTML = "";
|
|
2116
2354
|
singleSec.style.minHeight = `${pageH}px`;
|
|
2117
|
-
singleSec.style.maxHeight = `${pageH}px`;
|
|
2118
|
-
singleSec.style.overflow = "hidden";
|
|
2119
2355
|
singleSec.style.boxSizing = "border-box";
|
|
2356
|
+
let curContent = contentContainer;
|
|
2120
2357
|
let curSec = singleSec;
|
|
2121
2358
|
let curH = 0;
|
|
2122
|
-
const maxH = pageH -
|
|
2359
|
+
const maxH = pageH - 140;
|
|
2360
|
+
finalSections.push(singleSec);
|
|
2123
2361
|
for (let i = 0; i < children.length; i++) {
|
|
2124
2362
|
const child = children[i];
|
|
2125
|
-
|
|
2126
|
-
|
|
2363
|
+
const chH = childHeights[i];
|
|
2364
|
+
curContent.appendChild(child);
|
|
2127
2365
|
curH += chH;
|
|
2128
2366
|
if (curH >= maxH && i < children.length - 1) {
|
|
2129
2367
|
const nextSec = document.createElement("section");
|
|
2130
2368
|
nextSec.className = singleSec.className;
|
|
2131
2369
|
nextSec.style.cssText = singleSec.style.cssText;
|
|
2132
|
-
nextSec.style.width = singleSec.style.width || "816px";
|
|
2133
2370
|
nextSec.style.minHeight = `${pageH}px`;
|
|
2134
|
-
nextSec.style.maxHeight = `${pageH}px`;
|
|
2135
|
-
nextSec.style.overflow = "hidden";
|
|
2136
2371
|
nextSec.style.boxSizing = "border-box";
|
|
2137
2372
|
nextSec.style.backgroundColor = "#ffffff";
|
|
2138
2373
|
nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
2139
2374
|
nextSec.style.borderRadius = "4px";
|
|
2140
2375
|
nextSec.style.marginBottom = "24px";
|
|
2141
|
-
|
|
2142
|
-
|
|
2376
|
+
if (headerEl) {
|
|
2377
|
+
nextSec.appendChild(headerEl.cloneNode(true));
|
|
2378
|
+
}
|
|
2379
|
+
const nextArticle = document.createElement("article");
|
|
2380
|
+
if (contentContainer.tagName.toLowerCase() === "article") {
|
|
2381
|
+
nextArticle.style.cssText = contentContainer.style.cssText;
|
|
2382
|
+
}
|
|
2383
|
+
nextSec.appendChild(nextArticle);
|
|
2384
|
+
if (footerEl) {
|
|
2385
|
+
nextSec.appendChild(footerEl.cloneNode(true));
|
|
2386
|
+
}
|
|
2387
|
+
if (curSec.nextSibling) {
|
|
2388
|
+
parent.insertBefore(nextSec, curSec.nextSibling);
|
|
2389
|
+
} else {
|
|
2390
|
+
parent.appendChild(nextSec);
|
|
2391
|
+
}
|
|
2392
|
+
finalSections.push(nextSec);
|
|
2143
2393
|
curSec = nextSec;
|
|
2394
|
+
curContent = nextArticle;
|
|
2144
2395
|
curH = 0;
|
|
2145
2396
|
}
|
|
2146
2397
|
}
|
|
2147
|
-
|
|
2398
|
+
} else {
|
|
2399
|
+
finalSections.push(singleSec);
|
|
2148
2400
|
}
|
|
2149
2401
|
}
|
|
2402
|
+
sections = finalSections;
|
|
2150
2403
|
}
|
|
2151
2404
|
const pageElements = sections.length > 0 ? sections : cards;
|
|
2152
2405
|
const totalPages = Math.max(1, pageElements.length);
|
|
@@ -2184,6 +2437,7 @@ var DocxPlugin = class {
|
|
|
2184
2437
|
if (indicator) {
|
|
2185
2438
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2186
2439
|
}
|
|
2440
|
+
ctx.container.scrollTop = 0;
|
|
2187
2441
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2188
2442
|
};
|
|
2189
2443
|
if (totalPages > 1) {
|
|
@@ -2537,6 +2791,88 @@ var DocxPlugin = class {
|
|
|
2537
2791
|
}
|
|
2538
2792
|
return result;
|
|
2539
2793
|
}
|
|
2794
|
+
parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
|
|
2795
|
+
const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
|
|
2796
|
+
let categories = [];
|
|
2797
|
+
if (catMatches.length > 0) {
|
|
2798
|
+
categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
|
|
2799
|
+
}
|
|
2800
|
+
if (categories.length === 0) {
|
|
2801
|
+
categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
|
|
2802
|
+
}
|
|
2803
|
+
const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
|
|
2804
|
+
const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
|
|
2805
|
+
const series = [];
|
|
2806
|
+
sers.forEach((s, sIdx) => {
|
|
2807
|
+
const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
|
|
2808
|
+
const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
|
|
2809
|
+
const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
|
|
2810
|
+
const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
|
|
2811
|
+
const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
|
|
2812
|
+
let values = [];
|
|
2813
|
+
if (valMatch) {
|
|
2814
|
+
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);
|
|
2815
|
+
}
|
|
2816
|
+
series.push({ title, color, values });
|
|
2817
|
+
});
|
|
2818
|
+
if (series.length === 0) return "";
|
|
2819
|
+
let maxVal = 10;
|
|
2820
|
+
series.forEach((s) => s.values.forEach((v) => {
|
|
2821
|
+
if (v > maxVal) maxVal = v;
|
|
2822
|
+
}));
|
|
2823
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
2824
|
+
if (maxVal % 2 !== 0) maxVal++;
|
|
2825
|
+
const padLeft = 45;
|
|
2826
|
+
const padBottom = 55;
|
|
2827
|
+
const padTop = 20;
|
|
2828
|
+
const padRight = 20;
|
|
2829
|
+
const plotW = width - padLeft - padRight;
|
|
2830
|
+
const plotH = height - padTop - padBottom;
|
|
2831
|
+
const yTicks = 5;
|
|
2832
|
+
let gridLines = "";
|
|
2833
|
+
for (let i = 0; i <= yTicks; i++) {
|
|
2834
|
+
const val = maxVal / yTicks * i;
|
|
2835
|
+
const y = padTop + plotH - val / maxVal * plotH;
|
|
2836
|
+
gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
|
|
2837
|
+
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>`;
|
|
2838
|
+
}
|
|
2839
|
+
const numCats = categories.length;
|
|
2840
|
+
const numSers = series.length;
|
|
2841
|
+
const groupW = plotW / numCats;
|
|
2842
|
+
const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
|
|
2843
|
+
const groupPad = (groupW - barW * numSers) / 2;
|
|
2844
|
+
let bars = "";
|
|
2845
|
+
let catLabels = "";
|
|
2846
|
+
for (let c = 0; c < numCats; c++) {
|
|
2847
|
+
const catX = padLeft + c * groupW;
|
|
2848
|
+
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>`;
|
|
2849
|
+
for (let s = 0; s < numSers; s++) {
|
|
2850
|
+
const val = series[s].values[c] ?? 0;
|
|
2851
|
+
const bH = Math.max(0, val / maxVal * plotH);
|
|
2852
|
+
const bX = catX + groupPad + s * barW;
|
|
2853
|
+
const bY = padTop + plotH - bH;
|
|
2854
|
+
bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
let legend = "";
|
|
2858
|
+
const legY = height - 12;
|
|
2859
|
+
let legX = padLeft + (plotW - numSers * 100) / 2;
|
|
2860
|
+
series.forEach((s) => {
|
|
2861
|
+
legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
|
|
2862
|
+
legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
|
|
2863
|
+
legX += 95;
|
|
2864
|
+
});
|
|
2865
|
+
return `
|
|
2866
|
+
<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">
|
|
2867
|
+
${gridLines}
|
|
2868
|
+
<line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2869
|
+
<line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2870
|
+
${bars}
|
|
2871
|
+
${catLabels}
|
|
2872
|
+
${legend}
|
|
2873
|
+
</svg>
|
|
2874
|
+
`.trim();
|
|
2875
|
+
}
|
|
2540
2876
|
};
|
|
2541
2877
|
function docxPlugin() {
|
|
2542
2878
|
return new DocxPlugin();
|
|
@@ -3103,6 +3439,16 @@ var CodePlugin = class {
|
|
|
3103
3439
|
execute: () => {
|
|
3104
3440
|
instance.print?.();
|
|
3105
3441
|
}
|
|
3442
|
+
},
|
|
3443
|
+
{
|
|
3444
|
+
id: "open-window",
|
|
3445
|
+
icon: "open-window",
|
|
3446
|
+
label: "Open in Separate Full Window",
|
|
3447
|
+
type: "button",
|
|
3448
|
+
group: "actions",
|
|
3449
|
+
execute: () => {
|
|
3450
|
+
instance.openInSeparateWindow?.();
|
|
3451
|
+
}
|
|
3106
3452
|
}
|
|
3107
3453
|
);
|
|
3108
3454
|
return actions;
|
|
@@ -3116,17 +3462,39 @@ var CodePlugin = class {
|
|
|
3116
3462
|
let rawPages = [];
|
|
3117
3463
|
if (isTxt) {
|
|
3118
3464
|
const explicitPages = fullText.split(/(?:\f|\x0C)/);
|
|
3465
|
+
const charsPerLine = 85;
|
|
3466
|
+
const maxVisualLines = 45;
|
|
3467
|
+
const charsPerPage = charsPerLine * maxVisualLines;
|
|
3119
3468
|
for (const ep of explicitPages) {
|
|
3120
3469
|
const lines = ep.split(/\r?\n/);
|
|
3121
3470
|
let currentChunk = [];
|
|
3471
|
+
let currentLines = 0;
|
|
3122
3472
|
for (let i = 0; i < lines.length; i++) {
|
|
3123
|
-
|
|
3124
|
-
|
|
3473
|
+
const line = lines[i];
|
|
3474
|
+
const vLines = Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
|
|
3475
|
+
if (currentLines + vLines > maxVisualLines && currentChunk.length > 0) {
|
|
3125
3476
|
rawPages.push(currentChunk.join("\n"));
|
|
3126
3477
|
currentChunk = [];
|
|
3478
|
+
currentLines = 0;
|
|
3479
|
+
}
|
|
3480
|
+
if (vLines > maxVisualLines) {
|
|
3481
|
+
let remaining = line;
|
|
3482
|
+
while (remaining.length > charsPerPage) {
|
|
3483
|
+
let splitIdx = remaining.lastIndexOf(" ", charsPerPage);
|
|
3484
|
+
if (splitIdx < charsPerPage * 0.75) splitIdx = charsPerPage;
|
|
3485
|
+
rawPages.push(remaining.slice(0, splitIdx));
|
|
3486
|
+
remaining = remaining.slice(splitIdx).trimStart();
|
|
3487
|
+
}
|
|
3488
|
+
if (remaining.length > 0) {
|
|
3489
|
+
currentChunk.push(remaining);
|
|
3490
|
+
currentLines = Math.ceil(remaining.length / charsPerLine);
|
|
3491
|
+
}
|
|
3492
|
+
} else {
|
|
3493
|
+
currentChunk.push(line);
|
|
3494
|
+
currentLines += vLines;
|
|
3127
3495
|
}
|
|
3128
3496
|
}
|
|
3129
|
-
if (currentChunk.length > 0
|
|
3497
|
+
if (currentChunk.length > 0) {
|
|
3130
3498
|
rawPages.push(currentChunk.join("\n"));
|
|
3131
3499
|
}
|
|
3132
3500
|
}
|
|
@@ -3236,6 +3604,7 @@ var CodePlugin = class {
|
|
|
3236
3604
|
if (indicator) {
|
|
3237
3605
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3238
3606
|
}
|
|
3607
|
+
container.scrollTop = 0;
|
|
3239
3608
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3240
3609
|
};
|
|
3241
3610
|
if (totalPages > 1) {
|
|
@@ -4248,6 +4617,14 @@ var RtfPlugin = class {
|
|
|
4248
4617
|
type: "button",
|
|
4249
4618
|
group: "actions",
|
|
4250
4619
|
execute: () => instance.print?.()
|
|
4620
|
+
},
|
|
4621
|
+
{
|
|
4622
|
+
id: "open-window",
|
|
4623
|
+
icon: "open-window",
|
|
4624
|
+
label: "Open in Separate Full Window",
|
|
4625
|
+
type: "button",
|
|
4626
|
+
group: "actions",
|
|
4627
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4251
4628
|
}
|
|
4252
4629
|
);
|
|
4253
4630
|
return actions;
|
|
@@ -4300,11 +4677,55 @@ var RtfPlugin = class {
|
|
|
4300
4677
|
}
|
|
4301
4678
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
4302
4679
|
const htmlElements = await doc.render();
|
|
4303
|
-
|
|
4304
|
-
for (
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4680
|
+
const contentNodes = [];
|
|
4681
|
+
for (const item of htmlElements) {
|
|
4682
|
+
if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
|
|
4683
|
+
contentNodes.push(...Array.from(item.children));
|
|
4684
|
+
} else {
|
|
4685
|
+
contentNodes.push(item);
|
|
4686
|
+
}
|
|
4687
|
+
}
|
|
4688
|
+
wrapper.innerHTML = "";
|
|
4689
|
+
contentNodes.forEach((node) => wrapper.appendChild(node));
|
|
4690
|
+
const childHeights = contentNodes.map((c) => {
|
|
4691
|
+
const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
|
|
4692
|
+
const offH = c.offsetHeight || 0;
|
|
4693
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
4694
|
+
const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
|
|
4695
|
+
return Math.max(rectH, offH, estH);
|
|
4696
|
+
});
|
|
4697
|
+
wrapper.innerHTML = "";
|
|
4698
|
+
const createRtfCard = () => {
|
|
4699
|
+
const card = document.createElement("div");
|
|
4700
|
+
card.className = "fp-rtf-page-card";
|
|
4701
|
+
card.style.backgroundColor = "#ffffff";
|
|
4702
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
4703
|
+
card.style.borderRadius = "4px";
|
|
4704
|
+
card.style.padding = "72px 56px";
|
|
4705
|
+
card.style.width = "816px";
|
|
4706
|
+
card.style.minHeight = "1056px";
|
|
4707
|
+
card.style.boxSizing = "border-box";
|
|
4708
|
+
card.style.marginBottom = "24px";
|
|
4709
|
+
card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4710
|
+
card.style.lineHeight = "1.6";
|
|
4711
|
+
return card;
|
|
4712
|
+
};
|
|
4713
|
+
let curCard = createRtfCard();
|
|
4714
|
+
wrapper.appendChild(curCard);
|
|
4715
|
+
pageElements = [curCard];
|
|
4716
|
+
let curH = 0;
|
|
4717
|
+
const maxH = 912;
|
|
4718
|
+
for (let i = 0; i < contentNodes.length; i++) {
|
|
4719
|
+
const child = contentNodes[i];
|
|
4720
|
+
const chH = childHeights[i];
|
|
4721
|
+
curCard.appendChild(child);
|
|
4722
|
+
curH += chH;
|
|
4723
|
+
if (curH >= maxH && i < contentNodes.length - 1) {
|
|
4724
|
+
curCard = createRtfCard();
|
|
4725
|
+
wrapper.appendChild(curCard);
|
|
4726
|
+
pageElements.push(curCard);
|
|
4727
|
+
curH = 0;
|
|
4728
|
+
}
|
|
4308
4729
|
}
|
|
4309
4730
|
} catch (err) {
|
|
4310
4731
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
@@ -4370,6 +4791,7 @@ var RtfPlugin = class {
|
|
|
4370
4791
|
if (indicator) {
|
|
4371
4792
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4372
4793
|
}
|
|
4794
|
+
ctx.container.scrollTop = 0;
|
|
4373
4795
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4374
4796
|
};
|
|
4375
4797
|
if (totalPages > 1) {
|
|
@@ -4671,6 +5093,14 @@ var OpenDocumentPlugin = class {
|
|
|
4671
5093
|
type: "button",
|
|
4672
5094
|
group: "actions",
|
|
4673
5095
|
execute: () => instance.print?.()
|
|
5096
|
+
},
|
|
5097
|
+
{
|
|
5098
|
+
id: "open-window",
|
|
5099
|
+
icon: "open-window",
|
|
5100
|
+
label: "Open in Separate Full Window",
|
|
5101
|
+
type: "button",
|
|
5102
|
+
group: "actions",
|
|
5103
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4674
5104
|
}
|
|
4675
5105
|
);
|
|
4676
5106
|
return actions;
|
|
@@ -4841,12 +5271,9 @@ var OpenDocumentPlugin = class {
|
|
|
4841
5271
|
page.style.backgroundColor = "#ffffff";
|
|
4842
5272
|
page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
|
|
4843
5273
|
page.style.borderRadius = "4px";
|
|
4844
|
-
page.style.boxSizing = "border-box";
|
|
4845
5274
|
page.style.display = idx === 0 ? "block" : "none";
|
|
4846
|
-
page.style.
|
|
4847
|
-
page.style.
|
|
4848
|
-
page.style.left = "50%";
|
|
4849
|
-
page.style.transform = "translateX(-50%)";
|
|
5275
|
+
page.style.margin = "0 auto 24px";
|
|
5276
|
+
page.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
4850
5277
|
elements.forEach((el) => page.appendChild(el));
|
|
4851
5278
|
wrapper.appendChild(page);
|
|
4852
5279
|
slides.push(page);
|
|
@@ -4884,6 +5311,7 @@ var OpenDocumentPlugin = class {
|
|
|
4884
5311
|
if (indicator) {
|
|
4885
5312
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4886
5313
|
}
|
|
5314
|
+
container.scrollTop = 0;
|
|
4887
5315
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4888
5316
|
};
|
|
4889
5317
|
return {
|
|
@@ -5261,6 +5689,14 @@ var DocPlugin = class {
|
|
|
5261
5689
|
type: "button",
|
|
5262
5690
|
group: "actions",
|
|
5263
5691
|
execute: () => instance.print?.()
|
|
5692
|
+
},
|
|
5693
|
+
{
|
|
5694
|
+
id: "open-window",
|
|
5695
|
+
icon: "open-window",
|
|
5696
|
+
label: "Open in Separate Full Window",
|
|
5697
|
+
type: "button",
|
|
5698
|
+
group: "actions",
|
|
5699
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
5264
5700
|
}
|
|
5265
5701
|
);
|
|
5266
5702
|
return actions;
|
|
@@ -5376,6 +5812,7 @@ var DocPlugin = class {
|
|
|
5376
5812
|
if (indicator) {
|
|
5377
5813
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
5378
5814
|
}
|
|
5815
|
+
container.scrollTop = 0;
|
|
5379
5816
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
5380
5817
|
};
|
|
5381
5818
|
if (totalPages > 1) {
|
|
@@ -5551,47 +5988,69 @@ var DocPlugin = class {
|
|
|
5551
5988
|
heuristicTextExtraction(buffer) {
|
|
5552
5989
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5553
5990
|
}
|
|
5991
|
+
cleanWordDocFields(text) {
|
|
5992
|
+
if (!text) return "";
|
|
5993
|
+
let cleaned = text.replace(
|
|
5994
|
+
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
5995
|
+
(_match, url, label) => {
|
|
5996
|
+
const cleanUrl = url.trim();
|
|
5997
|
+
const cleanLabel = label.trim() || cleanUrl;
|
|
5998
|
+
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
5999
|
+
}
|
|
6000
|
+
);
|
|
6001
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, "$1");
|
|
6002
|
+
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6003
|
+
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6004
|
+
return cleaned;
|
|
6005
|
+
}
|
|
5554
6006
|
splitIntoPages(text) {
|
|
5555
6007
|
if (!text) return [""];
|
|
5556
|
-
const
|
|
5557
|
-
|
|
5558
|
-
const
|
|
6008
|
+
const cleanedText = this.cleanWordDocFields(text);
|
|
6009
|
+
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
6010
|
+
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);
|
|
6011
|
+
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
6012
|
+
const maxLinesPerPage = 32;
|
|
6013
|
+
const charsPerLine = 80;
|
|
5559
6014
|
const finalPages = [];
|
|
5560
6015
|
for (const part of explicitParts) {
|
|
5561
|
-
const lines = part.split(
|
|
6016
|
+
const lines = part.split("\n");
|
|
5562
6017
|
let currentLines = [];
|
|
5563
6018
|
let count = 0;
|
|
5564
6019
|
for (const line of lines) {
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
if (count
|
|
6020
|
+
const plainLine = line.replace(/<[^>]+>/g, "");
|
|
6021
|
+
const vLines = Math.max(1, Math.ceil((plainLine.length || 1) / charsPerLine));
|
|
6022
|
+
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
5568
6023
|
finalPages.push(currentLines.join("\n"));
|
|
5569
6024
|
currentLines = [];
|
|
5570
6025
|
count = 0;
|
|
5571
6026
|
}
|
|
6027
|
+
currentLines.push(line);
|
|
6028
|
+
count += vLines;
|
|
5572
6029
|
}
|
|
5573
6030
|
if (currentLines.length > 0) {
|
|
5574
6031
|
finalPages.push(currentLines.join("\n"));
|
|
5575
6032
|
}
|
|
5576
6033
|
}
|
|
5577
|
-
return finalPages.length > 0 ? finalPages : [
|
|
6034
|
+
return finalPages.length > 0 ? finalPages : [normalized];
|
|
5578
6035
|
}
|
|
5579
6036
|
/**
|
|
5580
6037
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
5581
6038
|
*/
|
|
5582
6039
|
formatDocToHtml(text, filename) {
|
|
5583
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
6040
|
+
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");
|
|
5584
6041
|
let html = "";
|
|
5585
6042
|
let inList = false;
|
|
5586
6043
|
let tableLines = [];
|
|
5587
6044
|
const flushTable = () => {
|
|
5588
6045
|
if (tableLines.length > 0) {
|
|
5589
|
-
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size:
|
|
5590
|
-
for (
|
|
5591
|
-
|
|
5592
|
-
const
|
|
6046
|
+
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13px; font-family: Calibri, sans-serif;">';
|
|
6047
|
+
for (let rIdx = 0; rIdx < tableLines.length; rIdx++) {
|
|
6048
|
+
const tLine = tableLines[rIdx];
|
|
6049
|
+
const isHeader = rIdx === 0;
|
|
6050
|
+
html += `<tr style="${isHeader ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
6051
|
+
const cols = tLine.split(" ").filter((c) => c.trim().length > 0);
|
|
5593
6052
|
for (const col of cols) {
|
|
5594
|
-
html += `<td style="border: 1px solid #cbd5e1; padding:
|
|
6053
|
+
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px;">${DOMPurify6.sanitize(col.trim())}</td>`;
|
|
5595
6054
|
}
|
|
5596
6055
|
html += "</tr>";
|
|
5597
6056
|
}
|
|
@@ -5604,27 +6063,18 @@ var DocPlugin = class {
|
|
|
5604
6063
|
let line = lines[i];
|
|
5605
6064
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
5606
6065
|
if (tabCount > 0) {
|
|
5607
|
-
let
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
const nextTabCount = (lines[j].match(/\t/g) || []).length;
|
|
5611
|
-
if (nextTabCount === tabCount) {
|
|
5612
|
-
consecutiveTableLines++;
|
|
5613
|
-
j++;
|
|
5614
|
-
} else {
|
|
5615
|
-
break;
|
|
5616
|
-
}
|
|
6066
|
+
let j = i;
|
|
6067
|
+
while (j < lines.length && (lines[j].match(/\t/g) || []).length > 0) {
|
|
6068
|
+
j++;
|
|
5617
6069
|
}
|
|
5618
|
-
if (
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
inList = false;
|
|
5622
|
-
}
|
|
5623
|
-
tableLines = lines.slice(i, j);
|
|
5624
|
-
flushTable();
|
|
5625
|
-
i = j;
|
|
5626
|
-
continue;
|
|
6070
|
+
if (inList) {
|
|
6071
|
+
html += "</ul>";
|
|
6072
|
+
inList = false;
|
|
5627
6073
|
}
|
|
6074
|
+
tableLines = lines.slice(i, j);
|
|
6075
|
+
flushTable();
|
|
6076
|
+
i = j;
|
|
6077
|
+
continue;
|
|
5628
6078
|
}
|
|
5629
6079
|
line = line.trim();
|
|
5630
6080
|
if (!line) {
|
|
@@ -5644,25 +6094,26 @@ var DocPlugin = class {
|
|
|
5644
6094
|
i++;
|
|
5645
6095
|
continue;
|
|
5646
6096
|
}
|
|
6097
|
+
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
5647
6098
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
5648
6099
|
if (inList) {
|
|
5649
6100
|
html += "</ul>";
|
|
5650
6101
|
inList = false;
|
|
5651
6102
|
}
|
|
5652
|
-
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>`;
|
|
6103
|
+
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>`;
|
|
5653
6104
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
5654
6105
|
if (!inList) {
|
|
5655
6106
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
5656
6107
|
inList = true;
|
|
5657
6108
|
}
|
|
5658
6109
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
5659
|
-
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
|
|
6110
|
+
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
|
|
5660
6111
|
} else {
|
|
5661
6112
|
if (inList) {
|
|
5662
6113
|
html += "</ul>";
|
|
5663
6114
|
inList = false;
|
|
5664
6115
|
}
|
|
5665
|
-
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
|
|
6116
|
+
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
|
|
5666
6117
|
}
|
|
5667
6118
|
i++;
|
|
5668
6119
|
}
|