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