@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/vue.js
CHANGED
|
@@ -337,16 +337,21 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
337
337
|
metadata.mimeType = source.type || void 0;
|
|
338
338
|
metadata.extension = extractExtension(source.name);
|
|
339
339
|
buffer = await source.arrayBuffer();
|
|
340
|
-
} else if (source instanceof Blob) {
|
|
340
|
+
} else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
|
|
341
341
|
metadata.size = source.size;
|
|
342
342
|
metadata.mimeType = source.type || void 0;
|
|
343
|
+
if (source.name) {
|
|
344
|
+
metadata.name = source.name;
|
|
345
|
+
metadata.extension = extractExtension(source.name);
|
|
346
|
+
}
|
|
343
347
|
buffer = await source.arrayBuffer();
|
|
344
|
-
} else if (source instanceof ArrayBuffer) {
|
|
348
|
+
} else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
|
|
345
349
|
buffer = source;
|
|
346
|
-
} else if (source instanceof Uint8Array) {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
+
} else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
|
|
351
|
+
const view = source;
|
|
352
|
+
buffer = view.buffer.slice(
|
|
353
|
+
view.byteOffset,
|
|
354
|
+
view.byteOffset + view.byteLength
|
|
350
355
|
);
|
|
351
356
|
} else {
|
|
352
357
|
throw new Error("Unsupported file source type");
|
|
@@ -431,6 +436,45 @@ function createElement(tag, attrs, ...children) {
|
|
|
431
436
|
}
|
|
432
437
|
return el;
|
|
433
438
|
}
|
|
439
|
+
var DB_NAME = "PreviewFileTransferDB";
|
|
440
|
+
var DB_STORE = "transfers";
|
|
441
|
+
function openDB() {
|
|
442
|
+
return new Promise((resolve, reject) => {
|
|
443
|
+
if (typeof indexedDB === "undefined") {
|
|
444
|
+
return reject(new Error("IndexedDB is not available"));
|
|
445
|
+
}
|
|
446
|
+
const req = indexedDB.open(DB_NAME, 1);
|
|
447
|
+
req.onupgradeneeded = () => {
|
|
448
|
+
const db = req.result;
|
|
449
|
+
if (!db.objectStoreNames.contains(DB_STORE)) {
|
|
450
|
+
db.createObjectStore(DB_STORE, { keyPath: "id" });
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
req.onsuccess = () => resolve(req.result);
|
|
454
|
+
req.onerror = () => reject(req.error);
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
async function saveTransferPayload(id, payload) {
|
|
458
|
+
if (typeof window !== "undefined") {
|
|
459
|
+
try {
|
|
460
|
+
window[id] = payload;
|
|
461
|
+
window.__lastTransfer = payload;
|
|
462
|
+
} catch {
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
try {
|
|
466
|
+
const db = await openDB();
|
|
467
|
+
return new Promise((resolve, reject) => {
|
|
468
|
+
const tx = db.transaction(DB_STORE, "readwrite");
|
|
469
|
+
const store = tx.objectStore(DB_STORE);
|
|
470
|
+
store.put({ id, ...payload, timestamp: Date.now() });
|
|
471
|
+
tx.oncomplete = () => resolve();
|
|
472
|
+
tx.onerror = () => reject(tx.error);
|
|
473
|
+
});
|
|
474
|
+
} catch (e) {
|
|
475
|
+
console.warn("[saveTransferPayload] IndexedDB store warning:", e);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
434
478
|
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>`;
|
|
435
479
|
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>`;
|
|
436
480
|
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>`;
|
|
@@ -450,6 +494,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
|
|
|
450
494
|
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>`;
|
|
451
495
|
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>`;
|
|
452
496
|
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>`;
|
|
497
|
+
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>`;
|
|
453
498
|
var ICON_MAP = {
|
|
454
499
|
"zoom-in": ICON_ZOOM_IN,
|
|
455
500
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -476,7 +521,9 @@ var ICON_MAP = {
|
|
|
476
521
|
"forward-10": ICON_FAST_FORWARD,
|
|
477
522
|
"rewind": ICON_REWIND,
|
|
478
523
|
"replay-10": ICON_REWIND,
|
|
479
|
-
"speed": ICON_SPEED
|
|
524
|
+
"speed": ICON_SPEED,
|
|
525
|
+
"open-window": ICON_EXTERNAL_WINDOW,
|
|
526
|
+
"external-window": ICON_EXTERNAL_WINDOW
|
|
480
527
|
};
|
|
481
528
|
var ToolbarController = class {
|
|
482
529
|
el;
|
|
@@ -693,7 +740,7 @@ var ThumbnailPanel = class {
|
|
|
693
740
|
}
|
|
694
741
|
}
|
|
695
742
|
};
|
|
696
|
-
var FilePreviewViewer = class {
|
|
743
|
+
var FilePreviewViewer = class _FilePreviewViewer {
|
|
697
744
|
plugins = [];
|
|
698
745
|
activeInstance = null;
|
|
699
746
|
abortController = null;
|
|
@@ -730,6 +777,7 @@ var FilePreviewViewer = class {
|
|
|
730
777
|
* Preview a file in the given container element.
|
|
731
778
|
*/
|
|
732
779
|
async preview(container, source, options = {}) {
|
|
780
|
+
this.currentOptions = options;
|
|
733
781
|
this.abort();
|
|
734
782
|
this.abortController = new AbortController();
|
|
735
783
|
const { signal } = this.abortController;
|
|
@@ -739,7 +787,10 @@ var FilePreviewViewer = class {
|
|
|
739
787
|
this.showLoading();
|
|
740
788
|
try {
|
|
741
789
|
const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
|
|
742
|
-
|
|
790
|
+
if (options.metadata) {
|
|
791
|
+
Object.assign(metadata, options.metadata);
|
|
792
|
+
}
|
|
793
|
+
this.currentBuffer = buffer.slice(0);
|
|
743
794
|
this.currentMetadata = metadata;
|
|
744
795
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
745
796
|
const fileInfo = { metadata, buffer };
|
|
@@ -769,6 +820,7 @@ var FilePreviewViewer = class {
|
|
|
769
820
|
}
|
|
770
821
|
});
|
|
771
822
|
this.activeInstance = instance;
|
|
823
|
+
instance.openInSeparateWindow = () => this.openInSeparateWindow();
|
|
772
824
|
this.hideLoading();
|
|
773
825
|
this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
|
|
774
826
|
if (options.showToolbar !== false && this.toolbar) {
|
|
@@ -778,26 +830,16 @@ var FilePreviewViewer = class {
|
|
|
778
830
|
actions.push({
|
|
779
831
|
id: "fullscreen",
|
|
780
832
|
icon: "fullscreen",
|
|
781
|
-
label: "
|
|
833
|
+
label: "Fullscreen",
|
|
782
834
|
type: "button",
|
|
783
835
|
group: "view",
|
|
784
|
-
execute:
|
|
836
|
+
execute: () => {
|
|
785
837
|
try {
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
if (this.wrapperEl?.requestFullscreen) {
|
|
790
|
-
await this.wrapperEl.requestFullscreen().catch(() => {
|
|
791
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
792
|
-
});
|
|
793
|
-
} else {
|
|
794
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
795
|
-
}
|
|
838
|
+
if (!document.fullscreenElement) {
|
|
839
|
+
this.wrapperEl?.requestFullscreen?.();
|
|
840
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
796
841
|
} else {
|
|
797
|
-
|
|
798
|
-
await document.exitFullscreen().catch(() => {
|
|
799
|
-
});
|
|
800
|
-
}
|
|
842
|
+
document.exitFullscreen?.();
|
|
801
843
|
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
802
844
|
}
|
|
803
845
|
} catch {
|
|
@@ -809,6 +851,28 @@ var FilePreviewViewer = class {
|
|
|
809
851
|
}
|
|
810
852
|
});
|
|
811
853
|
}
|
|
854
|
+
const openWinAction = actions.find((a) => a.id === "open-window");
|
|
855
|
+
if (openWinAction) {
|
|
856
|
+
if (options?._isSeparateWindow) {
|
|
857
|
+
const idx = actions.indexOf(openWinAction);
|
|
858
|
+
if (idx !== -1) actions.splice(idx, 1);
|
|
859
|
+
} else {
|
|
860
|
+
openWinAction.execute = () => {
|
|
861
|
+
this.openInSeparateWindow();
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
} else if (!options?._isSeparateWindow) {
|
|
865
|
+
actions.push({
|
|
866
|
+
id: "open-window",
|
|
867
|
+
icon: "open-window",
|
|
868
|
+
label: "Open in Separate Full Window",
|
|
869
|
+
type: "button",
|
|
870
|
+
group: "actions",
|
|
871
|
+
execute: () => {
|
|
872
|
+
this.openInSeparateWindow();
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
}
|
|
812
876
|
this.toolbar.update(actions);
|
|
813
877
|
this.toolbar.show();
|
|
814
878
|
}
|
|
@@ -841,6 +905,104 @@ var FilePreviewViewer = class {
|
|
|
841
905
|
throw error;
|
|
842
906
|
}
|
|
843
907
|
}
|
|
908
|
+
/**
|
|
909
|
+
* Opens the current file preview in a separate full browser window.
|
|
910
|
+
*/
|
|
911
|
+
openInSeparateWindow() {
|
|
912
|
+
if (!this.currentBuffer) {
|
|
913
|
+
console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
|
|
914
|
+
return null;
|
|
915
|
+
}
|
|
916
|
+
if (this.currentOptions.onOpenSeparateWindow) {
|
|
917
|
+
return this.currentOptions.onOpenSeparateWindow({
|
|
918
|
+
buffer: this.currentBuffer,
|
|
919
|
+
metadata: this.currentMetadata || { name: "Document" },
|
|
920
|
+
options: this.currentOptions
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
|
|
924
|
+
let clonedBuffer;
|
|
925
|
+
try {
|
|
926
|
+
clonedBuffer = this.currentBuffer.slice(0);
|
|
927
|
+
} catch {
|
|
928
|
+
clonedBuffer = this.currentBuffer;
|
|
929
|
+
}
|
|
930
|
+
const payload = {
|
|
931
|
+
buffer: clonedBuffer,
|
|
932
|
+
metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
|
|
933
|
+
options: { ...this.currentOptions, _isSeparateWindow: true }
|
|
934
|
+
};
|
|
935
|
+
if (typeof window !== "undefined") {
|
|
936
|
+
try {
|
|
937
|
+
window[transferId] = payload;
|
|
938
|
+
window.__lastTransfer = payload;
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
saveTransferPayload(transferId, payload).catch((err) => {
|
|
943
|
+
console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
|
|
944
|
+
});
|
|
945
|
+
let targetUrl = null;
|
|
946
|
+
if (this.currentOptions.standaloneViewerUrl) {
|
|
947
|
+
const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
|
|
948
|
+
u.searchParams.set("mode", "fullscreen");
|
|
949
|
+
u.searchParams.set("transferId", transferId);
|
|
950
|
+
targetUrl = u.toString();
|
|
951
|
+
} else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
|
|
952
|
+
const u = new URL(window.location.href);
|
|
953
|
+
u.searchParams.set("mode", "fullscreen");
|
|
954
|
+
u.searchParams.set("transferId", transferId);
|
|
955
|
+
targetUrl = u.toString();
|
|
956
|
+
}
|
|
957
|
+
if (targetUrl) {
|
|
958
|
+
const newWin2 = window.open(targetUrl, "_blank");
|
|
959
|
+
if (!newWin2) {
|
|
960
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
return newWin2;
|
|
964
|
+
}
|
|
965
|
+
const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
|
|
966
|
+
const newWin = window.open("", "_blank");
|
|
967
|
+
if (!newWin) {
|
|
968
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
newWin.document.title = title;
|
|
972
|
+
newWin.document.body.style.margin = "0";
|
|
973
|
+
newWin.document.body.style.padding = "0";
|
|
974
|
+
newWin.document.body.style.width = "100vw";
|
|
975
|
+
newWin.document.body.style.height = "100vh";
|
|
976
|
+
newWin.document.body.style.overflow = "hidden";
|
|
977
|
+
newWin.document.body.style.backgroundColor = "#f8fafc";
|
|
978
|
+
const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
|
|
979
|
+
headNodes.forEach((node) => {
|
|
980
|
+
newWin.document.head.appendChild(node.cloneNode(true));
|
|
981
|
+
});
|
|
982
|
+
const root = newWin.document.createElement("div");
|
|
983
|
+
root.id = "full-window-preview-root";
|
|
984
|
+
root.style.width = "100%";
|
|
985
|
+
root.style.height = "100%";
|
|
986
|
+
root.style.overflow = "hidden";
|
|
987
|
+
newWin.document.body.appendChild(root);
|
|
988
|
+
const separateViewer = new _FilePreviewViewer();
|
|
989
|
+
for (const plugin of this.plugins) {
|
|
990
|
+
separateViewer.registerPlugin(plugin);
|
|
991
|
+
}
|
|
992
|
+
separateViewer.preview(root, this.currentBuffer.slice(0), {
|
|
993
|
+
...this.currentOptions,
|
|
994
|
+
showToolbar: true,
|
|
995
|
+
toolbarPosition: "top",
|
|
996
|
+
metadata: this.currentMetadata || void 0,
|
|
997
|
+
_isSeparateWindow: true
|
|
998
|
+
}).catch((err) => {
|
|
999
|
+
console.error("[FilePreviewViewer] Error rendering in separate window:", err);
|
|
1000
|
+
});
|
|
1001
|
+
newWin.addEventListener("beforeunload", () => {
|
|
1002
|
+
separateViewer.destroy();
|
|
1003
|
+
});
|
|
1004
|
+
return newWin;
|
|
1005
|
+
}
|
|
844
1006
|
/**
|
|
845
1007
|
* Subscribe to viewer events.
|
|
846
1008
|
*/
|
|
@@ -1246,8 +1408,20 @@ var CfbfReader = class {
|
|
|
1246
1408
|
}
|
|
1247
1409
|
};
|
|
1248
1410
|
if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
|
|
1249
|
-
if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1250
|
-
|
|
1411
|
+
if (!pdfjsLib.GlobalWorkerOptions.workerPort && !pdfjsLib.GlobalWorkerOptions.workerSrc) {
|
|
1412
|
+
const customWorker = window.__PDF_WORKER_SRC__;
|
|
1413
|
+
if (customWorker) {
|
|
1414
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = customWorker;
|
|
1415
|
+
} else {
|
|
1416
|
+
try {
|
|
1417
|
+
pdfjsLib.GlobalWorkerOptions.workerPort = new Worker(
|
|
1418
|
+
new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url),
|
|
1419
|
+
{ type: "module" }
|
|
1420
|
+
);
|
|
1421
|
+
} catch {
|
|
1422
|
+
pdfjsLib.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1251
1425
|
}
|
|
1252
1426
|
}
|
|
1253
1427
|
var PdfPlugin = class {
|
|
@@ -1341,6 +1515,14 @@ var PdfPlugin = class {
|
|
|
1341
1515
|
type: "button",
|
|
1342
1516
|
group: "actions",
|
|
1343
1517
|
execute: () => instance.print?.()
|
|
1518
|
+
},
|
|
1519
|
+
{
|
|
1520
|
+
id: "open-window",
|
|
1521
|
+
icon: "open-window",
|
|
1522
|
+
label: "Open in Separate Full Window",
|
|
1523
|
+
type: "button",
|
|
1524
|
+
group: "actions",
|
|
1525
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
1344
1526
|
}
|
|
1345
1527
|
];
|
|
1346
1528
|
}
|
|
@@ -1390,18 +1572,21 @@ var PdfPlugin = class {
|
|
|
1390
1572
|
indicator.style.pointerEvents = "none";
|
|
1391
1573
|
container.appendChild(indicator);
|
|
1392
1574
|
ctx.container.appendChild(container);
|
|
1575
|
+
const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
|
|
1576
|
+
const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
|
|
1393
1577
|
const loadingTask = pdfjsLib.getDocument({
|
|
1394
|
-
data: new Uint8Array(ctx.buffer),
|
|
1395
|
-
cMapUrl:
|
|
1578
|
+
data: new Uint8Array(ctx.buffer.slice(0)),
|
|
1579
|
+
cMapUrl: cmapsUrl,
|
|
1396
1580
|
cMapPacked: true,
|
|
1397
|
-
standardFontDataUrl:
|
|
1581
|
+
standardFontDataUrl: standardFontsUrl,
|
|
1582
|
+
verbosity: 0
|
|
1398
1583
|
});
|
|
1399
1584
|
const pdfDoc = await loadingTask.promise;
|
|
1400
1585
|
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1401
1586
|
let currentPage = 1;
|
|
1402
1587
|
let zoomScale = 1;
|
|
1403
1588
|
let rotation = 0;
|
|
1404
|
-
let fitMode = "
|
|
1589
|
+
let fitMode = "page";
|
|
1405
1590
|
let currentRenderTask = null;
|
|
1406
1591
|
const renderPage = async (pageNum) => {
|
|
1407
1592
|
if (currentRenderTask) {
|
|
@@ -1421,15 +1606,15 @@ var PdfPlugin = class {
|
|
|
1421
1606
|
const containerWidth = container.clientWidth || 900;
|
|
1422
1607
|
const containerHeight = container.clientHeight || 700;
|
|
1423
1608
|
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1424
|
-
const availWidth = Math.max(
|
|
1425
|
-
const availHeight = Math.max(
|
|
1609
|
+
const availWidth = Math.max(320, containerWidth - 48);
|
|
1610
|
+
const availHeight = Math.max(550, containerHeight - 88);
|
|
1426
1611
|
const scaleW = availWidth / unscaledVp.width;
|
|
1427
1612
|
const scaleH = availHeight / unscaledVp.height;
|
|
1428
1613
|
let fitScale;
|
|
1429
1614
|
if (fitMode === "page") {
|
|
1430
|
-
fitScale = Math.max(0.
|
|
1615
|
+
fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
|
|
1431
1616
|
} else {
|
|
1432
|
-
fitScale = Math.max(0.65, Math.min(1.
|
|
1617
|
+
fitScale = Math.max(0.65, Math.min(1.25, scaleW));
|
|
1433
1618
|
}
|
|
1434
1619
|
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1435
1620
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
@@ -1455,7 +1640,7 @@ var PdfPlugin = class {
|
|
|
1455
1640
|
currentRenderTask = null;
|
|
1456
1641
|
}
|
|
1457
1642
|
};
|
|
1458
|
-
|
|
1643
|
+
renderPage(1);
|
|
1459
1644
|
let resizeTimer = null;
|
|
1460
1645
|
const resizeObserver = new ResizeObserver(() => {
|
|
1461
1646
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -1499,7 +1684,7 @@ var PdfPlugin = class {
|
|
|
1499
1684
|
renderPage(currentPage);
|
|
1500
1685
|
},
|
|
1501
1686
|
fitToPage: () => {
|
|
1502
|
-
fitMode = fitMode === "
|
|
1687
|
+
fitMode = fitMode === "page" ? "width" : "page";
|
|
1503
1688
|
zoomScale = 1;
|
|
1504
1689
|
rotation = 0;
|
|
1505
1690
|
renderPage(currentPage);
|
|
@@ -1516,6 +1701,7 @@ var PdfPlugin = class {
|
|
|
1516
1701
|
getPageCount: () => totalPages,
|
|
1517
1702
|
getCurrentPage: () => currentPage,
|
|
1518
1703
|
goToPage: (page) => {
|
|
1704
|
+
container.scrollTop = 0;
|
|
1519
1705
|
renderPage(page);
|
|
1520
1706
|
},
|
|
1521
1707
|
download: () => {
|
|
@@ -1975,6 +2161,14 @@ var DocxPlugin = class {
|
|
|
1975
2161
|
type: "button",
|
|
1976
2162
|
group: "actions",
|
|
1977
2163
|
execute: () => instance.print?.()
|
|
2164
|
+
},
|
|
2165
|
+
{
|
|
2166
|
+
id: "open-window",
|
|
2167
|
+
icon: "open-window",
|
|
2168
|
+
label: "Open in Separate Full Window",
|
|
2169
|
+
type: "button",
|
|
2170
|
+
group: "actions",
|
|
2171
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
1978
2172
|
}
|
|
1979
2173
|
);
|
|
1980
2174
|
return actions;
|
|
@@ -2020,13 +2214,45 @@ var DocxPlugin = class {
|
|
|
2020
2214
|
ignoreFonts: true,
|
|
2021
2215
|
// Avoid crashes on embedded obfuscated fonts
|
|
2022
2216
|
breakPages: true,
|
|
2023
|
-
experimental: true
|
|
2217
|
+
experimental: true,
|
|
2218
|
+
ignoreLastRenderedPageBreak: false,
|
|
2219
|
+
// Honor Word's exact page breaks!
|
|
2220
|
+
renderHeaders: true,
|
|
2221
|
+
renderFooters: true,
|
|
2222
|
+
renderFootnotes: true,
|
|
2223
|
+
renderEndnotes: true,
|
|
2224
|
+
useBase64URL: true
|
|
2024
2225
|
});
|
|
2025
2226
|
if (!ctx.container.contains(wrapper)) {
|
|
2026
2227
|
ctx.container.appendChild(wrapper);
|
|
2027
2228
|
}
|
|
2028
2229
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
2029
2230
|
renderedSuccessfully = true;
|
|
2231
|
+
try {
|
|
2232
|
+
const unzipped = unzipSync(new Uint8Array(ctx.buffer));
|
|
2233
|
+
const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
|
|
2234
|
+
if (chartKeys.length > 0) {
|
|
2235
|
+
const allDivs = Array.from(wrapper.querySelectorAll("div"));
|
|
2236
|
+
const emptyContainers = allDivs.filter((div) => {
|
|
2237
|
+
const st = div.getAttribute("style") || "";
|
|
2238
|
+
return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
|
|
2239
|
+
});
|
|
2240
|
+
chartKeys.forEach((cKey, idx) => {
|
|
2241
|
+
const target = emptyContainers[idx];
|
|
2242
|
+
if (target) {
|
|
2243
|
+
const xmlStr = strFromU8(unzipped[cKey]);
|
|
2244
|
+
const svg = this.parseAndRenderChartSvg(xmlStr);
|
|
2245
|
+
if (svg) {
|
|
2246
|
+
target.innerHTML = svg;
|
|
2247
|
+
target.style.display = "block";
|
|
2248
|
+
target.style.margin = "12px auto";
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
});
|
|
2252
|
+
}
|
|
2253
|
+
} catch (chartErr) {
|
|
2254
|
+
console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
|
|
2255
|
+
}
|
|
2030
2256
|
}
|
|
2031
2257
|
} catch (err) {
|
|
2032
2258
|
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
@@ -2041,11 +2267,14 @@ var DocxPlugin = class {
|
|
|
2041
2267
|
renderedSuccessfully = true;
|
|
2042
2268
|
} catch (fallbackErr) {
|
|
2043
2269
|
console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
|
|
2270
|
+
const isCorrupt = fallbackErr?.message?.includes("invalid zip") || fallbackErr?.message?.includes("corrupted");
|
|
2044
2271
|
wrapper.innerHTML = `
|
|
2045
|
-
<div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
|
|
2046
|
-
<div style="font-size:48px; margin-bottom: 16px;"
|
|
2047
|
-
<h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2048
|
-
<p style="color: #64748b; margin: 0;
|
|
2272
|
+
<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;">
|
|
2273
|
+
<div style="font-size:48px; margin-bottom: 16px;">${isCorrupt ? "\u26A0\uFE0F" : "\u{1F4C4}"}</div>
|
|
2274
|
+
<h3 style="margin: 0 0 8px; color: #1e293b; font-size: 18px;">${ctx.metadata.name || "Word Document"}</h3>
|
|
2275
|
+
<p style="color: #64748b; margin: 0 0 16px; font-size: 14px; line-height: 1.5;">
|
|
2276
|
+
${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."}
|
|
2277
|
+
</p>
|
|
2049
2278
|
</div>
|
|
2050
2279
|
`;
|
|
2051
2280
|
if (!ctx.container.contains(wrapper)) {
|
|
@@ -2055,50 +2284,74 @@ var DocxPlugin = class {
|
|
|
2055
2284
|
}
|
|
2056
2285
|
let sections = Array.from(wrapper.querySelectorAll("section.docx"));
|
|
2057
2286
|
const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
|
|
2058
|
-
if (sections.length
|
|
2059
|
-
const
|
|
2060
|
-
const
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
const
|
|
2064
|
-
|
|
2287
|
+
if (sections.length > 0 && cards.length === 0) {
|
|
2288
|
+
const finalSections = [];
|
|
2289
|
+
for (const singleSec of sections) {
|
|
2290
|
+
const contentContainer = singleSec.querySelector("article") || singleSec;
|
|
2291
|
+
const children = Array.from(contentContainer.children);
|
|
2292
|
+
const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
|
|
2293
|
+
const secH = singleSec.scrollHeight || singleSec.offsetHeight;
|
|
2294
|
+
if (secH > pageH * 1.25 && children.length > 1) {
|
|
2295
|
+
const childHeights = children.map((c) => {
|
|
2296
|
+
const rectH = c.getBoundingClientRect().height;
|
|
2297
|
+
const offH = c.offsetHeight;
|
|
2298
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
2299
|
+
const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
|
|
2300
|
+
return Math.max(rectH, offH, estH);
|
|
2301
|
+
});
|
|
2065
2302
|
const parent = singleSec.parentElement || wrapper;
|
|
2066
|
-
const
|
|
2067
|
-
|
|
2303
|
+
const headerEl = singleSec.querySelector("header");
|
|
2304
|
+
const footerEl = singleSec.querySelector("footer");
|
|
2305
|
+
contentContainer.innerHTML = "";
|
|
2068
2306
|
singleSec.style.minHeight = `${pageH}px`;
|
|
2069
|
-
singleSec.style.maxHeight = `${pageH}px`;
|
|
2070
|
-
singleSec.style.overflow = "hidden";
|
|
2071
2307
|
singleSec.style.boxSizing = "border-box";
|
|
2308
|
+
let curContent = contentContainer;
|
|
2072
2309
|
let curSec = singleSec;
|
|
2073
2310
|
let curH = 0;
|
|
2074
|
-
const maxH = pageH -
|
|
2311
|
+
const maxH = pageH - 140;
|
|
2312
|
+
finalSections.push(singleSec);
|
|
2075
2313
|
for (let i = 0; i < children.length; i++) {
|
|
2076
2314
|
const child = children[i];
|
|
2077
|
-
|
|
2078
|
-
|
|
2315
|
+
const chH = childHeights[i];
|
|
2316
|
+
curContent.appendChild(child);
|
|
2079
2317
|
curH += chH;
|
|
2080
2318
|
if (curH >= maxH && i < children.length - 1) {
|
|
2081
2319
|
const nextSec = document.createElement("section");
|
|
2082
2320
|
nextSec.className = singleSec.className;
|
|
2083
2321
|
nextSec.style.cssText = singleSec.style.cssText;
|
|
2084
|
-
nextSec.style.width = singleSec.style.width || "816px";
|
|
2085
2322
|
nextSec.style.minHeight = `${pageH}px`;
|
|
2086
|
-
nextSec.style.maxHeight = `${pageH}px`;
|
|
2087
|
-
nextSec.style.overflow = "hidden";
|
|
2088
2323
|
nextSec.style.boxSizing = "border-box";
|
|
2089
2324
|
nextSec.style.backgroundColor = "#ffffff";
|
|
2090
2325
|
nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
2091
2326
|
nextSec.style.borderRadius = "4px";
|
|
2092
2327
|
nextSec.style.marginBottom = "24px";
|
|
2093
|
-
|
|
2094
|
-
|
|
2328
|
+
if (headerEl) {
|
|
2329
|
+
nextSec.appendChild(headerEl.cloneNode(true));
|
|
2330
|
+
}
|
|
2331
|
+
const nextArticle = document.createElement("article");
|
|
2332
|
+
if (contentContainer.tagName.toLowerCase() === "article") {
|
|
2333
|
+
nextArticle.style.cssText = contentContainer.style.cssText;
|
|
2334
|
+
}
|
|
2335
|
+
nextSec.appendChild(nextArticle);
|
|
2336
|
+
if (footerEl) {
|
|
2337
|
+
nextSec.appendChild(footerEl.cloneNode(true));
|
|
2338
|
+
}
|
|
2339
|
+
if (curSec.nextSibling) {
|
|
2340
|
+
parent.insertBefore(nextSec, curSec.nextSibling);
|
|
2341
|
+
} else {
|
|
2342
|
+
parent.appendChild(nextSec);
|
|
2343
|
+
}
|
|
2344
|
+
finalSections.push(nextSec);
|
|
2095
2345
|
curSec = nextSec;
|
|
2346
|
+
curContent = nextArticle;
|
|
2096
2347
|
curH = 0;
|
|
2097
2348
|
}
|
|
2098
2349
|
}
|
|
2099
|
-
|
|
2350
|
+
} else {
|
|
2351
|
+
finalSections.push(singleSec);
|
|
2100
2352
|
}
|
|
2101
2353
|
}
|
|
2354
|
+
sections = finalSections;
|
|
2102
2355
|
}
|
|
2103
2356
|
const pageElements = sections.length > 0 ? sections : cards;
|
|
2104
2357
|
const totalPages = Math.max(1, pageElements.length);
|
|
@@ -2136,6 +2389,7 @@ var DocxPlugin = class {
|
|
|
2136
2389
|
if (indicator) {
|
|
2137
2390
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2138
2391
|
}
|
|
2392
|
+
ctx.container.scrollTop = 0;
|
|
2139
2393
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2140
2394
|
};
|
|
2141
2395
|
if (totalPages > 1) {
|
|
@@ -2489,6 +2743,88 @@ var DocxPlugin = class {
|
|
|
2489
2743
|
}
|
|
2490
2744
|
return result;
|
|
2491
2745
|
}
|
|
2746
|
+
parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
|
|
2747
|
+
const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
|
|
2748
|
+
let categories = [];
|
|
2749
|
+
if (catMatches.length > 0) {
|
|
2750
|
+
categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
|
|
2751
|
+
}
|
|
2752
|
+
if (categories.length === 0) {
|
|
2753
|
+
categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
|
|
2754
|
+
}
|
|
2755
|
+
const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
|
|
2756
|
+
const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
|
|
2757
|
+
const series = [];
|
|
2758
|
+
sers.forEach((s, sIdx) => {
|
|
2759
|
+
const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
|
|
2760
|
+
const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
|
|
2761
|
+
const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
|
|
2762
|
+
const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
|
|
2763
|
+
const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
|
|
2764
|
+
let values = [];
|
|
2765
|
+
if (valMatch) {
|
|
2766
|
+
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);
|
|
2767
|
+
}
|
|
2768
|
+
series.push({ title, color, values });
|
|
2769
|
+
});
|
|
2770
|
+
if (series.length === 0) return "";
|
|
2771
|
+
let maxVal = 10;
|
|
2772
|
+
series.forEach((s) => s.values.forEach((v) => {
|
|
2773
|
+
if (v > maxVal) maxVal = v;
|
|
2774
|
+
}));
|
|
2775
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
2776
|
+
if (maxVal % 2 !== 0) maxVal++;
|
|
2777
|
+
const padLeft = 45;
|
|
2778
|
+
const padBottom = 55;
|
|
2779
|
+
const padTop = 20;
|
|
2780
|
+
const padRight = 20;
|
|
2781
|
+
const plotW = width - padLeft - padRight;
|
|
2782
|
+
const plotH = height - padTop - padBottom;
|
|
2783
|
+
const yTicks = 5;
|
|
2784
|
+
let gridLines = "";
|
|
2785
|
+
for (let i = 0; i <= yTicks; i++) {
|
|
2786
|
+
const val = maxVal / yTicks * i;
|
|
2787
|
+
const y = padTop + plotH - val / maxVal * plotH;
|
|
2788
|
+
gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
|
|
2789
|
+
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>`;
|
|
2790
|
+
}
|
|
2791
|
+
const numCats = categories.length;
|
|
2792
|
+
const numSers = series.length;
|
|
2793
|
+
const groupW = plotW / numCats;
|
|
2794
|
+
const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
|
|
2795
|
+
const groupPad = (groupW - barW * numSers) / 2;
|
|
2796
|
+
let bars = "";
|
|
2797
|
+
let catLabels = "";
|
|
2798
|
+
for (let c = 0; c < numCats; c++) {
|
|
2799
|
+
const catX = padLeft + c * groupW;
|
|
2800
|
+
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>`;
|
|
2801
|
+
for (let s = 0; s < numSers; s++) {
|
|
2802
|
+
const val = series[s].values[c] ?? 0;
|
|
2803
|
+
const bH = Math.max(0, val / maxVal * plotH);
|
|
2804
|
+
const bX = catX + groupPad + s * barW;
|
|
2805
|
+
const bY = padTop + plotH - bH;
|
|
2806
|
+
bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
let legend = "";
|
|
2810
|
+
const legY = height - 12;
|
|
2811
|
+
let legX = padLeft + (plotW - numSers * 100) / 2;
|
|
2812
|
+
series.forEach((s) => {
|
|
2813
|
+
legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
|
|
2814
|
+
legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
|
|
2815
|
+
legX += 95;
|
|
2816
|
+
});
|
|
2817
|
+
return `
|
|
2818
|
+
<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">
|
|
2819
|
+
${gridLines}
|
|
2820
|
+
<line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2821
|
+
<line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2822
|
+
${bars}
|
|
2823
|
+
${catLabels}
|
|
2824
|
+
${legend}
|
|
2825
|
+
</svg>
|
|
2826
|
+
`.trim();
|
|
2827
|
+
}
|
|
2492
2828
|
};
|
|
2493
2829
|
function docxPlugin() {
|
|
2494
2830
|
return new DocxPlugin();
|
|
@@ -3055,6 +3391,16 @@ var CodePlugin = class {
|
|
|
3055
3391
|
execute: () => {
|
|
3056
3392
|
instance.print?.();
|
|
3057
3393
|
}
|
|
3394
|
+
},
|
|
3395
|
+
{
|
|
3396
|
+
id: "open-window",
|
|
3397
|
+
icon: "open-window",
|
|
3398
|
+
label: "Open in Separate Full Window",
|
|
3399
|
+
type: "button",
|
|
3400
|
+
group: "actions",
|
|
3401
|
+
execute: () => {
|
|
3402
|
+
instance.openInSeparateWindow?.();
|
|
3403
|
+
}
|
|
3058
3404
|
}
|
|
3059
3405
|
);
|
|
3060
3406
|
return actions;
|
|
@@ -3068,17 +3414,39 @@ var CodePlugin = class {
|
|
|
3068
3414
|
let rawPages = [];
|
|
3069
3415
|
if (isTxt) {
|
|
3070
3416
|
const explicitPages = fullText.split(/(?:\f|\x0C)/);
|
|
3417
|
+
const charsPerLine = 85;
|
|
3418
|
+
const maxVisualLines = 45;
|
|
3419
|
+
const charsPerPage = charsPerLine * maxVisualLines;
|
|
3071
3420
|
for (const ep of explicitPages) {
|
|
3072
3421
|
const lines = ep.split(/\r?\n/);
|
|
3073
3422
|
let currentChunk = [];
|
|
3423
|
+
let currentLines = 0;
|
|
3074
3424
|
for (let i = 0; i < lines.length; i++) {
|
|
3075
|
-
|
|
3076
|
-
|
|
3425
|
+
const line = lines[i];
|
|
3426
|
+
const vLines = Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
|
|
3427
|
+
if (currentLines + vLines > maxVisualLines && currentChunk.length > 0) {
|
|
3077
3428
|
rawPages.push(currentChunk.join("\n"));
|
|
3078
3429
|
currentChunk = [];
|
|
3430
|
+
currentLines = 0;
|
|
3431
|
+
}
|
|
3432
|
+
if (vLines > maxVisualLines) {
|
|
3433
|
+
let remaining = line;
|
|
3434
|
+
while (remaining.length > charsPerPage) {
|
|
3435
|
+
let splitIdx = remaining.lastIndexOf(" ", charsPerPage);
|
|
3436
|
+
if (splitIdx < charsPerPage * 0.75) splitIdx = charsPerPage;
|
|
3437
|
+
rawPages.push(remaining.slice(0, splitIdx));
|
|
3438
|
+
remaining = remaining.slice(splitIdx).trimStart();
|
|
3439
|
+
}
|
|
3440
|
+
if (remaining.length > 0) {
|
|
3441
|
+
currentChunk.push(remaining);
|
|
3442
|
+
currentLines = Math.ceil(remaining.length / charsPerLine);
|
|
3443
|
+
}
|
|
3444
|
+
} else {
|
|
3445
|
+
currentChunk.push(line);
|
|
3446
|
+
currentLines += vLines;
|
|
3079
3447
|
}
|
|
3080
3448
|
}
|
|
3081
|
-
if (currentChunk.length > 0
|
|
3449
|
+
if (currentChunk.length > 0) {
|
|
3082
3450
|
rawPages.push(currentChunk.join("\n"));
|
|
3083
3451
|
}
|
|
3084
3452
|
}
|
|
@@ -3188,6 +3556,7 @@ var CodePlugin = class {
|
|
|
3188
3556
|
if (indicator) {
|
|
3189
3557
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3190
3558
|
}
|
|
3559
|
+
container.scrollTop = 0;
|
|
3191
3560
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3192
3561
|
};
|
|
3193
3562
|
if (totalPages > 1) {
|
|
@@ -4200,6 +4569,14 @@ var RtfPlugin = class {
|
|
|
4200
4569
|
type: "button",
|
|
4201
4570
|
group: "actions",
|
|
4202
4571
|
execute: () => instance.print?.()
|
|
4572
|
+
},
|
|
4573
|
+
{
|
|
4574
|
+
id: "open-window",
|
|
4575
|
+
icon: "open-window",
|
|
4576
|
+
label: "Open in Separate Full Window",
|
|
4577
|
+
type: "button",
|
|
4578
|
+
group: "actions",
|
|
4579
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4203
4580
|
}
|
|
4204
4581
|
);
|
|
4205
4582
|
return actions;
|
|
@@ -4252,11 +4629,55 @@ var RtfPlugin = class {
|
|
|
4252
4629
|
}
|
|
4253
4630
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
4254
4631
|
const htmlElements = await doc.render();
|
|
4255
|
-
|
|
4256
|
-
for (
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4632
|
+
const contentNodes = [];
|
|
4633
|
+
for (const item of htmlElements) {
|
|
4634
|
+
if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
|
|
4635
|
+
contentNodes.push(...Array.from(item.children));
|
|
4636
|
+
} else {
|
|
4637
|
+
contentNodes.push(item);
|
|
4638
|
+
}
|
|
4639
|
+
}
|
|
4640
|
+
wrapper.innerHTML = "";
|
|
4641
|
+
contentNodes.forEach((node) => wrapper.appendChild(node));
|
|
4642
|
+
const childHeights = contentNodes.map((c) => {
|
|
4643
|
+
const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
|
|
4644
|
+
const offH = c.offsetHeight || 0;
|
|
4645
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
4646
|
+
const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
|
|
4647
|
+
return Math.max(rectH, offH, estH);
|
|
4648
|
+
});
|
|
4649
|
+
wrapper.innerHTML = "";
|
|
4650
|
+
const createRtfCard = () => {
|
|
4651
|
+
const card = document.createElement("div");
|
|
4652
|
+
card.className = "fp-rtf-page-card";
|
|
4653
|
+
card.style.backgroundColor = "#ffffff";
|
|
4654
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
4655
|
+
card.style.borderRadius = "4px";
|
|
4656
|
+
card.style.padding = "72px 56px";
|
|
4657
|
+
card.style.width = "816px";
|
|
4658
|
+
card.style.minHeight = "1056px";
|
|
4659
|
+
card.style.boxSizing = "border-box";
|
|
4660
|
+
card.style.marginBottom = "24px";
|
|
4661
|
+
card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4662
|
+
card.style.lineHeight = "1.6";
|
|
4663
|
+
return card;
|
|
4664
|
+
};
|
|
4665
|
+
let curCard = createRtfCard();
|
|
4666
|
+
wrapper.appendChild(curCard);
|
|
4667
|
+
pageElements = [curCard];
|
|
4668
|
+
let curH = 0;
|
|
4669
|
+
const maxH = 912;
|
|
4670
|
+
for (let i = 0; i < contentNodes.length; i++) {
|
|
4671
|
+
const child = contentNodes[i];
|
|
4672
|
+
const chH = childHeights[i];
|
|
4673
|
+
curCard.appendChild(child);
|
|
4674
|
+
curH += chH;
|
|
4675
|
+
if (curH >= maxH && i < contentNodes.length - 1) {
|
|
4676
|
+
curCard = createRtfCard();
|
|
4677
|
+
wrapper.appendChild(curCard);
|
|
4678
|
+
pageElements.push(curCard);
|
|
4679
|
+
curH = 0;
|
|
4680
|
+
}
|
|
4260
4681
|
}
|
|
4261
4682
|
} catch (err) {
|
|
4262
4683
|
console.warn("[RtfPlugin] RTF render error, fallback text:", err);
|
|
@@ -4322,6 +4743,7 @@ var RtfPlugin = class {
|
|
|
4322
4743
|
if (indicator) {
|
|
4323
4744
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4324
4745
|
}
|
|
4746
|
+
ctx.container.scrollTop = 0;
|
|
4325
4747
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4326
4748
|
};
|
|
4327
4749
|
if (totalPages > 1) {
|
|
@@ -4623,6 +5045,14 @@ var OpenDocumentPlugin = class {
|
|
|
4623
5045
|
type: "button",
|
|
4624
5046
|
group: "actions",
|
|
4625
5047
|
execute: () => instance.print?.()
|
|
5048
|
+
},
|
|
5049
|
+
{
|
|
5050
|
+
id: "open-window",
|
|
5051
|
+
icon: "open-window",
|
|
5052
|
+
label: "Open in Separate Full Window",
|
|
5053
|
+
type: "button",
|
|
5054
|
+
group: "actions",
|
|
5055
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4626
5056
|
}
|
|
4627
5057
|
);
|
|
4628
5058
|
return actions;
|
|
@@ -4793,12 +5223,9 @@ var OpenDocumentPlugin = class {
|
|
|
4793
5223
|
page.style.backgroundColor = "#ffffff";
|
|
4794
5224
|
page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
|
|
4795
5225
|
page.style.borderRadius = "4px";
|
|
4796
|
-
page.style.boxSizing = "border-box";
|
|
4797
5226
|
page.style.display = idx === 0 ? "block" : "none";
|
|
4798
|
-
page.style.
|
|
4799
|
-
page.style.
|
|
4800
|
-
page.style.left = "50%";
|
|
4801
|
-
page.style.transform = "translateX(-50%)";
|
|
5227
|
+
page.style.margin = "0 auto 24px";
|
|
5228
|
+
page.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
4802
5229
|
elements.forEach((el) => page.appendChild(el));
|
|
4803
5230
|
wrapper.appendChild(page);
|
|
4804
5231
|
slides.push(page);
|
|
@@ -4836,6 +5263,7 @@ var OpenDocumentPlugin = class {
|
|
|
4836
5263
|
if (indicator) {
|
|
4837
5264
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4838
5265
|
}
|
|
5266
|
+
container.scrollTop = 0;
|
|
4839
5267
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4840
5268
|
};
|
|
4841
5269
|
return {
|
|
@@ -5213,6 +5641,14 @@ var DocPlugin = class {
|
|
|
5213
5641
|
type: "button",
|
|
5214
5642
|
group: "actions",
|
|
5215
5643
|
execute: () => instance.print?.()
|
|
5644
|
+
},
|
|
5645
|
+
{
|
|
5646
|
+
id: "open-window",
|
|
5647
|
+
icon: "open-window",
|
|
5648
|
+
label: "Open in Separate Full Window",
|
|
5649
|
+
type: "button",
|
|
5650
|
+
group: "actions",
|
|
5651
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
5216
5652
|
}
|
|
5217
5653
|
);
|
|
5218
5654
|
return actions;
|
|
@@ -5328,6 +5764,7 @@ var DocPlugin = class {
|
|
|
5328
5764
|
if (indicator) {
|
|
5329
5765
|
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
5330
5766
|
}
|
|
5767
|
+
container.scrollTop = 0;
|
|
5331
5768
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
5332
5769
|
};
|
|
5333
5770
|
if (totalPages > 1) {
|
|
@@ -5503,47 +5940,69 @@ var DocPlugin = class {
|
|
|
5503
5940
|
heuristicTextExtraction(buffer) {
|
|
5504
5941
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5505
5942
|
}
|
|
5943
|
+
cleanWordDocFields(text) {
|
|
5944
|
+
if (!text) return "";
|
|
5945
|
+
let cleaned = text.replace(
|
|
5946
|
+
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
5947
|
+
(_match, url, label) => {
|
|
5948
|
+
const cleanUrl = url.trim();
|
|
5949
|
+
const cleanLabel = label.trim() || cleanUrl;
|
|
5950
|
+
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
5951
|
+
}
|
|
5952
|
+
);
|
|
5953
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, "$1");
|
|
5954
|
+
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
5955
|
+
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
5956
|
+
return cleaned;
|
|
5957
|
+
}
|
|
5506
5958
|
splitIntoPages(text) {
|
|
5507
5959
|
if (!text) return [""];
|
|
5508
|
-
const
|
|
5509
|
-
|
|
5510
|
-
const
|
|
5960
|
+
const cleanedText = this.cleanWordDocFields(text);
|
|
5961
|
+
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
5962
|
+
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);
|
|
5963
|
+
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
5964
|
+
const maxLinesPerPage = 32;
|
|
5965
|
+
const charsPerLine = 80;
|
|
5511
5966
|
const finalPages = [];
|
|
5512
5967
|
for (const part of explicitParts) {
|
|
5513
|
-
const lines = part.split(
|
|
5968
|
+
const lines = part.split("\n");
|
|
5514
5969
|
let currentLines = [];
|
|
5515
5970
|
let count = 0;
|
|
5516
5971
|
for (const line of lines) {
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
if (count
|
|
5972
|
+
const plainLine = line.replace(/<[^>]+>/g, "");
|
|
5973
|
+
const vLines = Math.max(1, Math.ceil((plainLine.length || 1) / charsPerLine));
|
|
5974
|
+
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
5520
5975
|
finalPages.push(currentLines.join("\n"));
|
|
5521
5976
|
currentLines = [];
|
|
5522
5977
|
count = 0;
|
|
5523
5978
|
}
|
|
5979
|
+
currentLines.push(line);
|
|
5980
|
+
count += vLines;
|
|
5524
5981
|
}
|
|
5525
5982
|
if (currentLines.length > 0) {
|
|
5526
5983
|
finalPages.push(currentLines.join("\n"));
|
|
5527
5984
|
}
|
|
5528
5985
|
}
|
|
5529
|
-
return finalPages.length > 0 ? finalPages : [
|
|
5986
|
+
return finalPages.length > 0 ? finalPages : [normalized];
|
|
5530
5987
|
}
|
|
5531
5988
|
/**
|
|
5532
5989
|
* Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
|
|
5533
5990
|
*/
|
|
5534
5991
|
formatDocToHtml(text, filename) {
|
|
5535
|
-
const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
|
|
5992
|
+
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");
|
|
5536
5993
|
let html = "";
|
|
5537
5994
|
let inList = false;
|
|
5538
5995
|
let tableLines = [];
|
|
5539
5996
|
const flushTable = () => {
|
|
5540
5997
|
if (tableLines.length > 0) {
|
|
5541
|
-
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size:
|
|
5542
|
-
for (
|
|
5543
|
-
|
|
5544
|
-
const
|
|
5998
|
+
html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13px; font-family: Calibri, sans-serif;">';
|
|
5999
|
+
for (let rIdx = 0; rIdx < tableLines.length; rIdx++) {
|
|
6000
|
+
const tLine = tableLines[rIdx];
|
|
6001
|
+
const isHeader = rIdx === 0;
|
|
6002
|
+
html += `<tr style="${isHeader ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
|
|
6003
|
+
const cols = tLine.split(" ").filter((c) => c.trim().length > 0);
|
|
5545
6004
|
for (const col of cols) {
|
|
5546
|
-
html += `<td style="border: 1px solid #cbd5e1; padding:
|
|
6005
|
+
html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px;">${DOMPurify6.sanitize(col.trim())}</td>`;
|
|
5547
6006
|
}
|
|
5548
6007
|
html += "</tr>";
|
|
5549
6008
|
}
|
|
@@ -5556,27 +6015,18 @@ var DocPlugin = class {
|
|
|
5556
6015
|
let line = lines[i];
|
|
5557
6016
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
5558
6017
|
if (tabCount > 0) {
|
|
5559
|
-
let
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
const nextTabCount = (lines[j].match(/\t/g) || []).length;
|
|
5563
|
-
if (nextTabCount === tabCount) {
|
|
5564
|
-
consecutiveTableLines++;
|
|
5565
|
-
j++;
|
|
5566
|
-
} else {
|
|
5567
|
-
break;
|
|
5568
|
-
}
|
|
6018
|
+
let j = i;
|
|
6019
|
+
while (j < lines.length && (lines[j].match(/\t/g) || []).length > 0) {
|
|
6020
|
+
j++;
|
|
5569
6021
|
}
|
|
5570
|
-
if (
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
inList = false;
|
|
5574
|
-
}
|
|
5575
|
-
tableLines = lines.slice(i, j);
|
|
5576
|
-
flushTable();
|
|
5577
|
-
i = j;
|
|
5578
|
-
continue;
|
|
6022
|
+
if (inList) {
|
|
6023
|
+
html += "</ul>";
|
|
6024
|
+
inList = false;
|
|
5579
6025
|
}
|
|
6026
|
+
tableLines = lines.slice(i, j);
|
|
6027
|
+
flushTable();
|
|
6028
|
+
i = j;
|
|
6029
|
+
continue;
|
|
5580
6030
|
}
|
|
5581
6031
|
line = line.trim();
|
|
5582
6032
|
if (!line) {
|
|
@@ -5596,25 +6046,26 @@ var DocPlugin = class {
|
|
|
5596
6046
|
i++;
|
|
5597
6047
|
continue;
|
|
5598
6048
|
}
|
|
6049
|
+
const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
|
|
5599
6050
|
if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
|
|
5600
6051
|
if (inList) {
|
|
5601
6052
|
html += "</ul>";
|
|
5602
6053
|
inList = false;
|
|
5603
6054
|
}
|
|
5604
|
-
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>`;
|
|
6055
|
+
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>`;
|
|
5605
6056
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
5606
6057
|
if (!inList) {
|
|
5607
6058
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
5608
6059
|
inList = true;
|
|
5609
6060
|
}
|
|
5610
6061
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
5611
|
-
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
|
|
6062
|
+
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
|
|
5612
6063
|
} else {
|
|
5613
6064
|
if (inList) {
|
|
5614
6065
|
html += "</ul>";
|
|
5615
6066
|
inList = false;
|
|
5616
6067
|
}
|
|
5617
|
-
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
|
|
6068
|
+
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
|
|
5618
6069
|
}
|
|
5619
6070
|
i++;
|
|
5620
6071
|
}
|