@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/index.cjs CHANGED
@@ -14,6 +14,7 @@ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
14
14
  var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
15
15
  var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
16
16
 
17
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
17
18
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
18
19
 
19
20
  function _interopNamespace(e) {
@@ -366,16 +367,21 @@ async function sourceToArrayBuffer(source, signal) {
366
367
  metadata.mimeType = source.type || void 0;
367
368
  metadata.extension = extractExtension(source.name);
368
369
  buffer = await source.arrayBuffer();
369
- } else if (source instanceof Blob) {
370
+ } else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
370
371
  metadata.size = source.size;
371
372
  metadata.mimeType = source.type || void 0;
373
+ if (source.name) {
374
+ metadata.name = source.name;
375
+ metadata.extension = extractExtension(source.name);
376
+ }
372
377
  buffer = await source.arrayBuffer();
373
- } else if (source instanceof ArrayBuffer) {
378
+ } else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
374
379
  buffer = source;
375
- } else if (source instanceof Uint8Array) {
376
- buffer = source.buffer.slice(
377
- source.byteOffset,
378
- source.byteOffset + source.byteLength
380
+ } else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
381
+ const view = source;
382
+ buffer = view.buffer.slice(
383
+ view.byteOffset,
384
+ view.byteOffset + view.byteLength
379
385
  );
380
386
  } else {
381
387
  throw new Error("Unsupported file source type");
@@ -504,6 +510,86 @@ function createElement(tag, attrs, ...children) {
504
510
  }
505
511
  return el;
506
512
  }
513
+ var DB_NAME = "PreviewFileTransferDB";
514
+ var DB_STORE = "transfers";
515
+ function openDB() {
516
+ return new Promise((resolve, reject) => {
517
+ if (typeof indexedDB === "undefined") {
518
+ return reject(new Error("IndexedDB is not available"));
519
+ }
520
+ const req = indexedDB.open(DB_NAME, 1);
521
+ req.onupgradeneeded = () => {
522
+ const db = req.result;
523
+ if (!db.objectStoreNames.contains(DB_STORE)) {
524
+ db.createObjectStore(DB_STORE, { keyPath: "id" });
525
+ }
526
+ };
527
+ req.onsuccess = () => resolve(req.result);
528
+ req.onerror = () => reject(req.error);
529
+ });
530
+ }
531
+ async function saveTransferPayload(id, payload) {
532
+ if (typeof window !== "undefined") {
533
+ try {
534
+ window[id] = payload;
535
+ window.__lastTransfer = payload;
536
+ } catch {
537
+ }
538
+ }
539
+ try {
540
+ const db = await openDB();
541
+ return new Promise((resolve, reject) => {
542
+ const tx = db.transaction(DB_STORE, "readwrite");
543
+ const store = tx.objectStore(DB_STORE);
544
+ store.put({ id, ...payload, timestamp: Date.now() });
545
+ tx.oncomplete = () => resolve();
546
+ tx.onerror = () => reject(tx.error);
547
+ });
548
+ } catch (e) {
549
+ console.warn("[saveTransferPayload] IndexedDB store warning:", e);
550
+ }
551
+ }
552
+ async function getTransferPayload(id) {
553
+ if (typeof window !== "undefined") {
554
+ try {
555
+ if (window.opener && window.opener[id]) {
556
+ return window.opener[id];
557
+ }
558
+ if (window[id]) {
559
+ return window[id];
560
+ }
561
+ if (window.opener && window.opener.__lastTransfer) {
562
+ return window.opener.__lastTransfer;
563
+ }
564
+ if (window.__lastTransfer) {
565
+ return window.__lastTransfer;
566
+ }
567
+ } catch {
568
+ }
569
+ }
570
+ try {
571
+ const db = await openDB();
572
+ return new Promise((resolve) => {
573
+ const tx = db.transaction(DB_STORE, "readonly");
574
+ const store = tx.objectStore(DB_STORE);
575
+ const req = store.get(id);
576
+ req.onsuccess = () => {
577
+ if (req.result && req.result.buffer) {
578
+ resolve({
579
+ buffer: req.result.buffer,
580
+ metadata: req.result.metadata,
581
+ options: req.result.options
582
+ });
583
+ } else {
584
+ resolve(null);
585
+ }
586
+ };
587
+ req.onerror = () => resolve(null);
588
+ });
589
+ } catch {
590
+ return null;
591
+ }
592
+ }
507
593
  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>`;
508
594
  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>`;
509
595
  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>`;
@@ -523,6 +609,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
523
609
  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>`;
524
610
  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>`;
525
611
  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>`;
612
+ 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>`;
526
613
  var ICON_MAP = {
527
614
  "zoom-in": ICON_ZOOM_IN,
528
615
  "zoom-out": ICON_ZOOM_OUT,
@@ -549,7 +636,9 @@ var ICON_MAP = {
549
636
  "forward-10": ICON_FAST_FORWARD,
550
637
  "rewind": ICON_REWIND,
551
638
  "replay-10": ICON_REWIND,
552
- "speed": ICON_SPEED
639
+ "speed": ICON_SPEED,
640
+ "open-window": ICON_EXTERNAL_WINDOW,
641
+ "external-window": ICON_EXTERNAL_WINDOW
553
642
  };
554
643
  var ToolbarController = class {
555
644
  el;
@@ -766,7 +855,7 @@ var ThumbnailPanel = class {
766
855
  }
767
856
  }
768
857
  };
769
- var FilePreviewViewer = class {
858
+ var FilePreviewViewer = class _FilePreviewViewer {
770
859
  plugins = [];
771
860
  activeInstance = null;
772
861
  abortController = null;
@@ -803,6 +892,7 @@ var FilePreviewViewer = class {
803
892
  * Preview a file in the given container element.
804
893
  */
805
894
  async preview(container, source, options = {}) {
895
+ this.currentOptions = options;
806
896
  this.abort();
807
897
  this.abortController = new AbortController();
808
898
  const { signal } = this.abortController;
@@ -812,7 +902,10 @@ var FilePreviewViewer = class {
812
902
  this.showLoading();
813
903
  try {
814
904
  const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
815
- this.currentBuffer = buffer;
905
+ if (options.metadata) {
906
+ Object.assign(metadata, options.metadata);
907
+ }
908
+ this.currentBuffer = buffer.slice(0);
816
909
  this.currentMetadata = metadata;
817
910
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
818
911
  const fileInfo = { metadata, buffer };
@@ -842,6 +935,7 @@ var FilePreviewViewer = class {
842
935
  }
843
936
  });
844
937
  this.activeInstance = instance;
938
+ instance.openInSeparateWindow = () => this.openInSeparateWindow();
845
939
  this.hideLoading();
846
940
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
847
941
  if (options.showToolbar !== false && this.toolbar) {
@@ -851,26 +945,16 @@ var FilePreviewViewer = class {
851
945
  actions.push({
852
946
  id: "fullscreen",
853
947
  icon: "fullscreen",
854
- label: "Toggle Fullscreen",
948
+ label: "Fullscreen",
855
949
  type: "button",
856
950
  group: "view",
857
- execute: async () => {
951
+ execute: () => {
858
952
  try {
859
- const isNativeFs = !!document.fullscreenElement;
860
- const isCssFs = this.wrapperEl?.classList.contains("fp-fullscreen-active");
861
- if (!isNativeFs && !isCssFs) {
862
- if (this.wrapperEl?.requestFullscreen) {
863
- await this.wrapperEl.requestFullscreen().catch(() => {
864
- this.wrapperEl?.classList.add("fp-fullscreen-active");
865
- });
866
- } else {
867
- this.wrapperEl?.classList.add("fp-fullscreen-active");
868
- }
953
+ if (!document.fullscreenElement) {
954
+ this.wrapperEl?.requestFullscreen?.();
955
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
869
956
  } else {
870
- if (document.fullscreenElement) {
871
- await document.exitFullscreen().catch(() => {
872
- });
873
- }
957
+ document.exitFullscreen?.();
874
958
  this.wrapperEl?.classList.remove("fp-fullscreen-active");
875
959
  }
876
960
  } catch {
@@ -882,6 +966,28 @@ var FilePreviewViewer = class {
882
966
  }
883
967
  });
884
968
  }
969
+ const openWinAction = actions.find((a) => a.id === "open-window");
970
+ if (openWinAction) {
971
+ if (options?._isSeparateWindow) {
972
+ const idx = actions.indexOf(openWinAction);
973
+ if (idx !== -1) actions.splice(idx, 1);
974
+ } else {
975
+ openWinAction.execute = () => {
976
+ this.openInSeparateWindow();
977
+ };
978
+ }
979
+ } else if (!options?._isSeparateWindow) {
980
+ actions.push({
981
+ id: "open-window",
982
+ icon: "open-window",
983
+ label: "Open in Separate Full Window",
984
+ type: "button",
985
+ group: "actions",
986
+ execute: () => {
987
+ this.openInSeparateWindow();
988
+ }
989
+ });
990
+ }
885
991
  this.toolbar.update(actions);
886
992
  this.toolbar.show();
887
993
  }
@@ -914,6 +1020,104 @@ var FilePreviewViewer = class {
914
1020
  throw error;
915
1021
  }
916
1022
  }
1023
+ /**
1024
+ * Opens the current file preview in a separate full browser window.
1025
+ */
1026
+ openInSeparateWindow() {
1027
+ if (!this.currentBuffer) {
1028
+ console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
1029
+ return null;
1030
+ }
1031
+ if (this.currentOptions.onOpenSeparateWindow) {
1032
+ return this.currentOptions.onOpenSeparateWindow({
1033
+ buffer: this.currentBuffer,
1034
+ metadata: this.currentMetadata || { name: "Document" },
1035
+ options: this.currentOptions
1036
+ });
1037
+ }
1038
+ const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
1039
+ let clonedBuffer;
1040
+ try {
1041
+ clonedBuffer = this.currentBuffer.slice(0);
1042
+ } catch {
1043
+ clonedBuffer = this.currentBuffer;
1044
+ }
1045
+ const payload = {
1046
+ buffer: clonedBuffer,
1047
+ metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
1048
+ options: { ...this.currentOptions, _isSeparateWindow: true }
1049
+ };
1050
+ if (typeof window !== "undefined") {
1051
+ try {
1052
+ window[transferId] = payload;
1053
+ window.__lastTransfer = payload;
1054
+ } catch {
1055
+ }
1056
+ }
1057
+ saveTransferPayload(transferId, payload).catch((err) => {
1058
+ console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
1059
+ });
1060
+ let targetUrl = null;
1061
+ if (this.currentOptions.standaloneViewerUrl) {
1062
+ const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
1063
+ u.searchParams.set("mode", "fullscreen");
1064
+ u.searchParams.set("transferId", transferId);
1065
+ targetUrl = u.toString();
1066
+ } else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
1067
+ const u = new URL(window.location.href);
1068
+ u.searchParams.set("mode", "fullscreen");
1069
+ u.searchParams.set("transferId", transferId);
1070
+ targetUrl = u.toString();
1071
+ }
1072
+ if (targetUrl) {
1073
+ const newWin2 = window.open(targetUrl, "_blank");
1074
+ if (!newWin2) {
1075
+ alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1076
+ return null;
1077
+ }
1078
+ return newWin2;
1079
+ }
1080
+ const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1081
+ const newWin = window.open("", "_blank");
1082
+ if (!newWin) {
1083
+ alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1084
+ return null;
1085
+ }
1086
+ newWin.document.title = title;
1087
+ newWin.document.body.style.margin = "0";
1088
+ newWin.document.body.style.padding = "0";
1089
+ newWin.document.body.style.width = "100vw";
1090
+ newWin.document.body.style.height = "100vh";
1091
+ newWin.document.body.style.overflow = "hidden";
1092
+ newWin.document.body.style.backgroundColor = "#f8fafc";
1093
+ const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
1094
+ headNodes.forEach((node) => {
1095
+ newWin.document.head.appendChild(node.cloneNode(true));
1096
+ });
1097
+ const root = newWin.document.createElement("div");
1098
+ root.id = "full-window-preview-root";
1099
+ root.style.width = "100%";
1100
+ root.style.height = "100%";
1101
+ root.style.overflow = "hidden";
1102
+ newWin.document.body.appendChild(root);
1103
+ const separateViewer = new _FilePreviewViewer();
1104
+ for (const plugin of this.plugins) {
1105
+ separateViewer.registerPlugin(plugin);
1106
+ }
1107
+ separateViewer.preview(root, this.currentBuffer.slice(0), {
1108
+ ...this.currentOptions,
1109
+ showToolbar: true,
1110
+ toolbarPosition: "top",
1111
+ metadata: this.currentMetadata || void 0,
1112
+ _isSeparateWindow: true
1113
+ }).catch((err) => {
1114
+ console.error("[FilePreviewViewer] Error rendering in separate window:", err);
1115
+ });
1116
+ newWin.addEventListener("beforeunload", () => {
1117
+ separateViewer.destroy();
1118
+ });
1119
+ return newWin;
1120
+ }
917
1121
  /**
918
1122
  * Subscribe to viewer events.
919
1123
  */
@@ -1319,8 +1523,20 @@ var CfbfReader = class {
1319
1523
  }
1320
1524
  };
1321
1525
  if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
1322
- if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1323
- pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1526
+ if (!pdfjsLib__namespace.GlobalWorkerOptions.workerPort && !pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1527
+ const customWorker = window.__PDF_WORKER_SRC__;
1528
+ if (customWorker) {
1529
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = customWorker;
1530
+ } else {
1531
+ try {
1532
+ pdfjsLib__namespace.GlobalWorkerOptions.workerPort = new Worker(
1533
+ new URL("pdfjs-dist/build/pdf.worker.min.mjs", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))),
1534
+ { type: "module" }
1535
+ );
1536
+ } catch {
1537
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
1538
+ }
1539
+ }
1324
1540
  }
1325
1541
  }
1326
1542
  var PdfPlugin = class {
@@ -1414,6 +1630,14 @@ var PdfPlugin = class {
1414
1630
  type: "button",
1415
1631
  group: "actions",
1416
1632
  execute: () => instance.print?.()
1633
+ },
1634
+ {
1635
+ id: "open-window",
1636
+ icon: "open-window",
1637
+ label: "Open in Separate Full Window",
1638
+ type: "button",
1639
+ group: "actions",
1640
+ execute: () => instance.openInSeparateWindow?.()
1417
1641
  }
1418
1642
  ];
1419
1643
  }
@@ -1463,18 +1687,21 @@ var PdfPlugin = class {
1463
1687
  indicator.style.pointerEvents = "none";
1464
1688
  container.appendChild(indicator);
1465
1689
  ctx.container.appendChild(container);
1690
+ const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1691
+ const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
1466
1692
  const loadingTask = pdfjsLib__namespace.getDocument({
1467
- data: new Uint8Array(ctx.buffer),
1468
- cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/cmaps/`,
1693
+ data: new Uint8Array(ctx.buffer.slice(0)),
1694
+ cMapUrl: cmapsUrl,
1469
1695
  cMapPacked: true,
1470
- standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/standard_fonts/`
1696
+ standardFontDataUrl: standardFontsUrl,
1697
+ verbosity: 0
1471
1698
  });
1472
1699
  const pdfDoc = await loadingTask.promise;
1473
1700
  const totalPages = Math.max(1, pdfDoc.numPages);
1474
1701
  let currentPage = 1;
1475
1702
  let zoomScale = 1;
1476
1703
  let rotation = 0;
1477
- let fitMode = "width";
1704
+ let fitMode = "page";
1478
1705
  let currentRenderTask = null;
1479
1706
  const renderPage = async (pageNum) => {
1480
1707
  if (currentRenderTask) {
@@ -1494,15 +1721,15 @@ var PdfPlugin = class {
1494
1721
  const containerWidth = container.clientWidth || 900;
1495
1722
  const containerHeight = container.clientHeight || 700;
1496
1723
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1497
- const availWidth = Math.max(280, containerWidth - 48);
1498
- const availHeight = Math.max(280, containerHeight - 88);
1724
+ const availWidth = Math.max(320, containerWidth - 48);
1725
+ const availHeight = Math.max(550, containerHeight - 88);
1499
1726
  const scaleW = availWidth / unscaledVp.width;
1500
1727
  const scaleH = availHeight / unscaledVp.height;
1501
1728
  let fitScale;
1502
1729
  if (fitMode === "page") {
1503
- fitScale = Math.max(0.5, Math.min(scaleW, scaleH));
1730
+ fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
1504
1731
  } else {
1505
- fitScale = Math.max(0.65, Math.min(1.15, scaleW));
1732
+ fitScale = Math.max(0.65, Math.min(1.25, scaleW));
1506
1733
  }
1507
1734
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1508
1735
  const pixelRatio = window.devicePixelRatio || 1;
@@ -1528,7 +1755,7 @@ var PdfPlugin = class {
1528
1755
  currentRenderTask = null;
1529
1756
  }
1530
1757
  };
1531
- await renderPage(1);
1758
+ renderPage(1);
1532
1759
  let resizeTimer = null;
1533
1760
  const resizeObserver = new ResizeObserver(() => {
1534
1761
  if (resizeTimer) clearTimeout(resizeTimer);
@@ -1572,7 +1799,7 @@ var PdfPlugin = class {
1572
1799
  renderPage(currentPage);
1573
1800
  },
1574
1801
  fitToPage: () => {
1575
- fitMode = fitMode === "width" ? "page" : "width";
1802
+ fitMode = fitMode === "page" ? "width" : "page";
1576
1803
  zoomScale = 1;
1577
1804
  rotation = 0;
1578
1805
  renderPage(currentPage);
@@ -1589,6 +1816,7 @@ var PdfPlugin = class {
1589
1816
  getPageCount: () => totalPages,
1590
1817
  getCurrentPage: () => currentPage,
1591
1818
  goToPage: (page) => {
1819
+ container.scrollTop = 0;
1592
1820
  renderPage(page);
1593
1821
  },
1594
1822
  download: () => {
@@ -2048,6 +2276,14 @@ var DocxPlugin = class {
2048
2276
  type: "button",
2049
2277
  group: "actions",
2050
2278
  execute: () => instance.print?.()
2279
+ },
2280
+ {
2281
+ id: "open-window",
2282
+ icon: "open-window",
2283
+ label: "Open in Separate Full Window",
2284
+ type: "button",
2285
+ group: "actions",
2286
+ execute: () => instance.openInSeparateWindow?.()
2051
2287
  }
2052
2288
  );
2053
2289
  return actions;
@@ -2093,13 +2329,45 @@ var DocxPlugin = class {
2093
2329
  ignoreFonts: true,
2094
2330
  // Avoid crashes on embedded obfuscated fonts
2095
2331
  breakPages: true,
2096
- experimental: true
2332
+ experimental: true,
2333
+ ignoreLastRenderedPageBreak: false,
2334
+ // Honor Word's exact page breaks!
2335
+ renderHeaders: true,
2336
+ renderFooters: true,
2337
+ renderFootnotes: true,
2338
+ renderEndnotes: true,
2339
+ useBase64URL: true
2097
2340
  });
2098
2341
  if (!ctx.container.contains(wrapper)) {
2099
2342
  ctx.container.appendChild(wrapper);
2100
2343
  }
2101
2344
  if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
2102
2345
  renderedSuccessfully = true;
2346
+ try {
2347
+ const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
2348
+ const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
2349
+ if (chartKeys.length > 0) {
2350
+ const allDivs = Array.from(wrapper.querySelectorAll("div"));
2351
+ const emptyContainers = allDivs.filter((div) => {
2352
+ const st = div.getAttribute("style") || "";
2353
+ return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
2354
+ });
2355
+ chartKeys.forEach((cKey, idx) => {
2356
+ const target = emptyContainers[idx];
2357
+ if (target) {
2358
+ const xmlStr = fflate.strFromU8(unzipped[cKey]);
2359
+ const svg = this.parseAndRenderChartSvg(xmlStr);
2360
+ if (svg) {
2361
+ target.innerHTML = svg;
2362
+ target.style.display = "block";
2363
+ target.style.margin = "12px auto";
2364
+ }
2365
+ }
2366
+ });
2367
+ }
2368
+ } catch (chartErr) {
2369
+ console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
2370
+ }
2103
2371
  }
2104
2372
  } catch (err) {
2105
2373
  console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
@@ -2114,11 +2382,14 @@ var DocxPlugin = class {
2114
2382
  renderedSuccessfully = true;
2115
2383
  } catch (fallbackErr) {
2116
2384
  console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
2385
+ const isCorrupt = fallbackErr?.message?.includes("invalid zip") || fallbackErr?.message?.includes("corrupted");
2117
2386
  wrapper.innerHTML = `
2118
- <div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
2119
- <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
2120
- <h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
2121
- <p style="color: #64748b; margin: 0;">Could not parse document content</p>
2387
+ <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;">
2388
+ <div style="font-size:48px; margin-bottom: 16px;">${isCorrupt ? "\u26A0\uFE0F" : "\u{1F4C4}"}</div>
2389
+ <h3 style="margin: 0 0 8px; color: #1e293b; font-size: 18px;">${ctx.metadata.name || "Word Document"}</h3>
2390
+ <p style="color: #64748b; margin: 0 0 16px; font-size: 14px; line-height: 1.5;">
2391
+ ${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."}
2392
+ </p>
2122
2393
  </div>
2123
2394
  `;
2124
2395
  if (!ctx.container.contains(wrapper)) {
@@ -2128,50 +2399,74 @@ var DocxPlugin = class {
2128
2399
  }
2129
2400
  let sections = Array.from(wrapper.querySelectorAll("section.docx"));
2130
2401
  const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
2131
- if (sections.length === 1 && cards.length === 0) {
2132
- const singleSec = sections[0];
2133
- const pageH = 1056;
2134
- const secH = singleSec.offsetHeight || singleSec.scrollHeight;
2135
- if (secH > pageH * 1.25) {
2136
- const children = Array.from(singleSec.children);
2137
- if (children.length > 1) {
2402
+ if (sections.length > 0 && cards.length === 0) {
2403
+ const finalSections = [];
2404
+ for (const singleSec of sections) {
2405
+ const contentContainer = singleSec.querySelector("article") || singleSec;
2406
+ const children = Array.from(contentContainer.children);
2407
+ const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
2408
+ const secH = singleSec.scrollHeight || singleSec.offsetHeight;
2409
+ if (secH > pageH * 1.25 && children.length > 1) {
2410
+ const childHeights = children.map((c) => {
2411
+ const rectH = c.getBoundingClientRect().height;
2412
+ const offH = c.offsetHeight;
2413
+ const textLen = c.textContent?.trim().length || 0;
2414
+ const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
2415
+ return Math.max(rectH, offH, estH);
2416
+ });
2138
2417
  const parent = singleSec.parentElement || wrapper;
2139
- const newSections = [singleSec];
2140
- singleSec.innerHTML = "";
2418
+ const headerEl = singleSec.querySelector("header");
2419
+ const footerEl = singleSec.querySelector("footer");
2420
+ contentContainer.innerHTML = "";
2141
2421
  singleSec.style.minHeight = `${pageH}px`;
2142
- singleSec.style.maxHeight = `${pageH}px`;
2143
- singleSec.style.overflow = "hidden";
2144
2422
  singleSec.style.boxSizing = "border-box";
2423
+ let curContent = contentContainer;
2145
2424
  let curSec = singleSec;
2146
2425
  let curH = 0;
2147
- const maxH = pageH - 96;
2426
+ const maxH = pageH - 140;
2427
+ finalSections.push(singleSec);
2148
2428
  for (let i = 0; i < children.length; i++) {
2149
2429
  const child = children[i];
2150
- curSec.appendChild(child);
2151
- const chH = child.offsetHeight || 28;
2430
+ const chH = childHeights[i];
2431
+ curContent.appendChild(child);
2152
2432
  curH += chH;
2153
2433
  if (curH >= maxH && i < children.length - 1) {
2154
2434
  const nextSec = document.createElement("section");
2155
2435
  nextSec.className = singleSec.className;
2156
2436
  nextSec.style.cssText = singleSec.style.cssText;
2157
- nextSec.style.width = singleSec.style.width || "816px";
2158
2437
  nextSec.style.minHeight = `${pageH}px`;
2159
- nextSec.style.maxHeight = `${pageH}px`;
2160
- nextSec.style.overflow = "hidden";
2161
2438
  nextSec.style.boxSizing = "border-box";
2162
2439
  nextSec.style.backgroundColor = "#ffffff";
2163
2440
  nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2164
2441
  nextSec.style.borderRadius = "4px";
2165
2442
  nextSec.style.marginBottom = "24px";
2166
- parent.appendChild(nextSec);
2167
- newSections.push(nextSec);
2443
+ if (headerEl) {
2444
+ nextSec.appendChild(headerEl.cloneNode(true));
2445
+ }
2446
+ const nextArticle = document.createElement("article");
2447
+ if (contentContainer.tagName.toLowerCase() === "article") {
2448
+ nextArticle.style.cssText = contentContainer.style.cssText;
2449
+ }
2450
+ nextSec.appendChild(nextArticle);
2451
+ if (footerEl) {
2452
+ nextSec.appendChild(footerEl.cloneNode(true));
2453
+ }
2454
+ if (curSec.nextSibling) {
2455
+ parent.insertBefore(nextSec, curSec.nextSibling);
2456
+ } else {
2457
+ parent.appendChild(nextSec);
2458
+ }
2459
+ finalSections.push(nextSec);
2168
2460
  curSec = nextSec;
2461
+ curContent = nextArticle;
2169
2462
  curH = 0;
2170
2463
  }
2171
2464
  }
2172
- sections = newSections;
2465
+ } else {
2466
+ finalSections.push(singleSec);
2173
2467
  }
2174
2468
  }
2469
+ sections = finalSections;
2175
2470
  }
2176
2471
  const pageElements = sections.length > 0 ? sections : cards;
2177
2472
  const totalPages = Math.max(1, pageElements.length);
@@ -2209,6 +2504,7 @@ var DocxPlugin = class {
2209
2504
  if (indicator) {
2210
2505
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2211
2506
  }
2507
+ ctx.container.scrollTop = 0;
2212
2508
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2213
2509
  };
2214
2510
  if (totalPages > 1) {
@@ -2562,6 +2858,88 @@ var DocxPlugin = class {
2562
2858
  }
2563
2859
  return result;
2564
2860
  }
2861
+ parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
2862
+ const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
2863
+ let categories = [];
2864
+ if (catMatches.length > 0) {
2865
+ categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
2866
+ }
2867
+ if (categories.length === 0) {
2868
+ categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
2869
+ }
2870
+ const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
2871
+ const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
2872
+ const series = [];
2873
+ sers.forEach((s, sIdx) => {
2874
+ const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
2875
+ const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
2876
+ const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
2877
+ const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
2878
+ const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
2879
+ let values = [];
2880
+ if (valMatch) {
2881
+ 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);
2882
+ }
2883
+ series.push({ title, color, values });
2884
+ });
2885
+ if (series.length === 0) return "";
2886
+ let maxVal = 10;
2887
+ series.forEach((s) => s.values.forEach((v) => {
2888
+ if (v > maxVal) maxVal = v;
2889
+ }));
2890
+ maxVal = Math.ceil(maxVal * 1.15);
2891
+ if (maxVal % 2 !== 0) maxVal++;
2892
+ const padLeft = 45;
2893
+ const padBottom = 55;
2894
+ const padTop = 20;
2895
+ const padRight = 20;
2896
+ const plotW = width - padLeft - padRight;
2897
+ const plotH = height - padTop - padBottom;
2898
+ const yTicks = 5;
2899
+ let gridLines = "";
2900
+ for (let i = 0; i <= yTicks; i++) {
2901
+ const val = maxVal / yTicks * i;
2902
+ const y = padTop + plotH - val / maxVal * plotH;
2903
+ gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
2904
+ 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>`;
2905
+ }
2906
+ const numCats = categories.length;
2907
+ const numSers = series.length;
2908
+ const groupW = plotW / numCats;
2909
+ const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
2910
+ const groupPad = (groupW - barW * numSers) / 2;
2911
+ let bars = "";
2912
+ let catLabels = "";
2913
+ for (let c = 0; c < numCats; c++) {
2914
+ const catX = padLeft + c * groupW;
2915
+ 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>`;
2916
+ for (let s = 0; s < numSers; s++) {
2917
+ const val = series[s].values[c] ?? 0;
2918
+ const bH = Math.max(0, val / maxVal * plotH);
2919
+ const bX = catX + groupPad + s * barW;
2920
+ const bY = padTop + plotH - bH;
2921
+ bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
2922
+ }
2923
+ }
2924
+ let legend = "";
2925
+ const legY = height - 12;
2926
+ let legX = padLeft + (plotW - numSers * 100) / 2;
2927
+ series.forEach((s) => {
2928
+ legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
2929
+ legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
2930
+ legX += 95;
2931
+ });
2932
+ return `
2933
+ <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">
2934
+ ${gridLines}
2935
+ <line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
2936
+ <line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
2937
+ ${bars}
2938
+ ${catLabels}
2939
+ ${legend}
2940
+ </svg>
2941
+ `.trim();
2942
+ }
2565
2943
  };
2566
2944
  function docxPlugin() {
2567
2945
  return new DocxPlugin();
@@ -3128,6 +3506,16 @@ var CodePlugin = class {
3128
3506
  execute: () => {
3129
3507
  instance.print?.();
3130
3508
  }
3509
+ },
3510
+ {
3511
+ id: "open-window",
3512
+ icon: "open-window",
3513
+ label: "Open in Separate Full Window",
3514
+ type: "button",
3515
+ group: "actions",
3516
+ execute: () => {
3517
+ instance.openInSeparateWindow?.();
3518
+ }
3131
3519
  }
3132
3520
  );
3133
3521
  return actions;
@@ -3141,17 +3529,39 @@ var CodePlugin = class {
3141
3529
  let rawPages = [];
3142
3530
  if (isTxt) {
3143
3531
  const explicitPages = fullText.split(/(?:\f|\x0C)/);
3532
+ const charsPerLine = 85;
3533
+ const maxVisualLines = 45;
3534
+ const charsPerPage = charsPerLine * maxVisualLines;
3144
3535
  for (const ep of explicitPages) {
3145
3536
  const lines = ep.split(/\r?\n/);
3146
3537
  let currentChunk = [];
3538
+ let currentLines = 0;
3147
3539
  for (let i = 0; i < lines.length; i++) {
3148
- currentChunk.push(lines[i]);
3149
- if (currentChunk.length >= 46) {
3540
+ const line = lines[i];
3541
+ const vLines = Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
3542
+ if (currentLines + vLines > maxVisualLines && currentChunk.length > 0) {
3150
3543
  rawPages.push(currentChunk.join("\n"));
3151
3544
  currentChunk = [];
3545
+ currentLines = 0;
3546
+ }
3547
+ if (vLines > maxVisualLines) {
3548
+ let remaining = line;
3549
+ while (remaining.length > charsPerPage) {
3550
+ let splitIdx = remaining.lastIndexOf(" ", charsPerPage);
3551
+ if (splitIdx < charsPerPage * 0.75) splitIdx = charsPerPage;
3552
+ rawPages.push(remaining.slice(0, splitIdx));
3553
+ remaining = remaining.slice(splitIdx).trimStart();
3554
+ }
3555
+ if (remaining.length > 0) {
3556
+ currentChunk.push(remaining);
3557
+ currentLines = Math.ceil(remaining.length / charsPerLine);
3558
+ }
3559
+ } else {
3560
+ currentChunk.push(line);
3561
+ currentLines += vLines;
3152
3562
  }
3153
3563
  }
3154
- if (currentChunk.length > 0 || lines.length === 0) {
3564
+ if (currentChunk.length > 0) {
3155
3565
  rawPages.push(currentChunk.join("\n"));
3156
3566
  }
3157
3567
  }
@@ -3261,6 +3671,7 @@ var CodePlugin = class {
3261
3671
  if (indicator) {
3262
3672
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3263
3673
  }
3674
+ container.scrollTop = 0;
3264
3675
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3265
3676
  };
3266
3677
  if (totalPages > 1) {
@@ -4273,6 +4684,14 @@ var RtfPlugin = class {
4273
4684
  type: "button",
4274
4685
  group: "actions",
4275
4686
  execute: () => instance.print?.()
4687
+ },
4688
+ {
4689
+ id: "open-window",
4690
+ icon: "open-window",
4691
+ label: "Open in Separate Full Window",
4692
+ type: "button",
4693
+ group: "actions",
4694
+ execute: () => instance.openInSeparateWindow?.()
4276
4695
  }
4277
4696
  );
4278
4697
  return actions;
@@ -4325,11 +4744,55 @@ var RtfPlugin = class {
4325
4744
  }
4326
4745
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
4327
4746
  const htmlElements = await doc.render();
4328
- pageElements = htmlElements;
4329
- for (let i = 0; i < htmlElements.length; i++) {
4330
- const el = htmlElements[i];
4331
- el.style.display = i === 0 ? "block" : "none";
4332
- wrapper.appendChild(el);
4747
+ const contentNodes = [];
4748
+ for (const item of htmlElements) {
4749
+ if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4750
+ contentNodes.push(...Array.from(item.children));
4751
+ } else {
4752
+ contentNodes.push(item);
4753
+ }
4754
+ }
4755
+ wrapper.innerHTML = "";
4756
+ contentNodes.forEach((node) => wrapper.appendChild(node));
4757
+ const childHeights = contentNodes.map((c) => {
4758
+ const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4759
+ const offH = c.offsetHeight || 0;
4760
+ const textLen = c.textContent?.trim().length || 0;
4761
+ const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
4762
+ return Math.max(rectH, offH, estH);
4763
+ });
4764
+ wrapper.innerHTML = "";
4765
+ const createRtfCard = () => {
4766
+ const card = document.createElement("div");
4767
+ card.className = "fp-rtf-page-card";
4768
+ card.style.backgroundColor = "#ffffff";
4769
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
4770
+ card.style.borderRadius = "4px";
4771
+ card.style.padding = "72px 56px";
4772
+ card.style.width = "816px";
4773
+ card.style.minHeight = "1056px";
4774
+ card.style.boxSizing = "border-box";
4775
+ card.style.marginBottom = "24px";
4776
+ card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4777
+ card.style.lineHeight = "1.6";
4778
+ return card;
4779
+ };
4780
+ let curCard = createRtfCard();
4781
+ wrapper.appendChild(curCard);
4782
+ pageElements = [curCard];
4783
+ let curH = 0;
4784
+ const maxH = 912;
4785
+ for (let i = 0; i < contentNodes.length; i++) {
4786
+ const child = contentNodes[i];
4787
+ const chH = childHeights[i];
4788
+ curCard.appendChild(child);
4789
+ curH += chH;
4790
+ if (curH >= maxH && i < contentNodes.length - 1) {
4791
+ curCard = createRtfCard();
4792
+ wrapper.appendChild(curCard);
4793
+ pageElements.push(curCard);
4794
+ curH = 0;
4795
+ }
4333
4796
  }
4334
4797
  } catch (err) {
4335
4798
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
@@ -4395,6 +4858,7 @@ var RtfPlugin = class {
4395
4858
  if (indicator) {
4396
4859
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4397
4860
  }
4861
+ ctx.container.scrollTop = 0;
4398
4862
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4399
4863
  };
4400
4864
  if (totalPages > 1) {
@@ -4696,6 +5160,14 @@ var OpenDocumentPlugin = class {
4696
5160
  type: "button",
4697
5161
  group: "actions",
4698
5162
  execute: () => instance.print?.()
5163
+ },
5164
+ {
5165
+ id: "open-window",
5166
+ icon: "open-window",
5167
+ label: "Open in Separate Full Window",
5168
+ type: "button",
5169
+ group: "actions",
5170
+ execute: () => instance.openInSeparateWindow?.()
4699
5171
  }
4700
5172
  );
4701
5173
  return actions;
@@ -4866,12 +5338,9 @@ var OpenDocumentPlugin = class {
4866
5338
  page.style.backgroundColor = "#ffffff";
4867
5339
  page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4868
5340
  page.style.borderRadius = "4px";
4869
- page.style.boxSizing = "border-box";
4870
5341
  page.style.display = idx === 0 ? "block" : "none";
4871
- page.style.position = "absolute";
4872
- page.style.top = "0";
4873
- page.style.left = "50%";
4874
- page.style.transform = "translateX(-50%)";
5342
+ page.style.margin = "0 auto 24px";
5343
+ page.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
4875
5344
  elements.forEach((el) => page.appendChild(el));
4876
5345
  wrapper.appendChild(page);
4877
5346
  slides.push(page);
@@ -4909,6 +5378,7 @@ var OpenDocumentPlugin = class {
4909
5378
  if (indicator) {
4910
5379
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4911
5380
  }
5381
+ container.scrollTop = 0;
4912
5382
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4913
5383
  };
4914
5384
  return {
@@ -5286,6 +5756,14 @@ var DocPlugin = class {
5286
5756
  type: "button",
5287
5757
  group: "actions",
5288
5758
  execute: () => instance.print?.()
5759
+ },
5760
+ {
5761
+ id: "open-window",
5762
+ icon: "open-window",
5763
+ label: "Open in Separate Full Window",
5764
+ type: "button",
5765
+ group: "actions",
5766
+ execute: () => instance.openInSeparateWindow?.()
5289
5767
  }
5290
5768
  );
5291
5769
  return actions;
@@ -5401,6 +5879,7 @@ var DocPlugin = class {
5401
5879
  if (indicator) {
5402
5880
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5403
5881
  }
5882
+ container.scrollTop = 0;
5404
5883
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5405
5884
  };
5406
5885
  if (totalPages > 1) {
@@ -5576,47 +6055,69 @@ var DocPlugin = class {
5576
6055
  heuristicTextExtraction(buffer) {
5577
6056
  return this.extractStringsFromBytes(new Uint8Array(buffer));
5578
6057
  }
6058
+ cleanWordDocFields(text) {
6059
+ if (!text) return "";
6060
+ let cleaned = text.replace(
6061
+ /\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
6062
+ (_match, url, label) => {
6063
+ const cleanUrl = url.trim();
6064
+ const cleanLabel = label.trim() || cleanUrl;
6065
+ return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
6066
+ }
6067
+ );
6068
+ cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, "$1");
6069
+ cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
6070
+ cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
6071
+ return cleaned;
6072
+ }
5579
6073
  splitIntoPages(text) {
5580
6074
  if (!text) return [""];
5581
- const explicitParts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
5582
- if (explicitParts.length === 0) explicitParts.push(text);
5583
- const maxLinesPerPage = 48;
6075
+ const cleanedText = this.cleanWordDocFields(text);
6076
+ const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
6077
+ 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);
6078
+ if (explicitParts.length === 0) explicitParts.push(normalized);
6079
+ const maxLinesPerPage = 32;
6080
+ const charsPerLine = 80;
5584
6081
  const finalPages = [];
5585
6082
  for (const part of explicitParts) {
5586
- const lines = part.split(/\r?\n/);
6083
+ const lines = part.split("\n");
5587
6084
  let currentLines = [];
5588
6085
  let count = 0;
5589
6086
  for (const line of lines) {
5590
- currentLines.push(line);
5591
- count++;
5592
- if (count >= maxLinesPerPage) {
6087
+ const plainLine = line.replace(/<[^>]+>/g, "");
6088
+ const vLines = Math.max(1, Math.ceil((plainLine.length || 1) / charsPerLine));
6089
+ if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
5593
6090
  finalPages.push(currentLines.join("\n"));
5594
6091
  currentLines = [];
5595
6092
  count = 0;
5596
6093
  }
6094
+ currentLines.push(line);
6095
+ count += vLines;
5597
6096
  }
5598
6097
  if (currentLines.length > 0) {
5599
6098
  finalPages.push(currentLines.join("\n"));
5600
6099
  }
5601
6100
  }
5602
- return finalPages.length > 0 ? finalPages : [text];
6101
+ return finalPages.length > 0 ? finalPages : [normalized];
5603
6102
  }
5604
6103
  /**
5605
6104
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
5606
6105
  */
5607
6106
  formatDocToHtml(text, filename) {
5608
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
6107
+ 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");
5609
6108
  let html = "";
5610
6109
  let inList = false;
5611
6110
  let tableLines = [];
5612
6111
  const flushTable = () => {
5613
6112
  if (tableLines.length > 0) {
5614
- html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5615
- for (const tLine of tableLines) {
5616
- html += "<tr>";
5617
- const cols = tLine.split(" ");
6113
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 13px; font-family: Calibri, sans-serif;">';
6114
+ for (let rIdx = 0; rIdx < tableLines.length; rIdx++) {
6115
+ const tLine = tableLines[rIdx];
6116
+ const isHeader = rIdx === 0;
6117
+ html += `<tr style="${isHeader ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
6118
+ const cols = tLine.split(" ").filter((c) => c.trim().length > 0);
5618
6119
  for (const col of cols) {
5619
- html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6__default.default.sanitize(col.trim())}</td>`;
6120
+ html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px;">${DOMPurify6__default.default.sanitize(col.trim())}</td>`;
5620
6121
  }
5621
6122
  html += "</tr>";
5622
6123
  }
@@ -5629,27 +6130,18 @@ var DocPlugin = class {
5629
6130
  let line = lines[i];
5630
6131
  let tabCount = (line.match(/\t/g) || []).length;
5631
6132
  if (tabCount > 0) {
5632
- let consecutiveTableLines = 1;
5633
- let j = i + 1;
5634
- while (j < lines.length) {
5635
- const nextTabCount = (lines[j].match(/\t/g) || []).length;
5636
- if (nextTabCount === tabCount) {
5637
- consecutiveTableLines++;
5638
- j++;
5639
- } else {
5640
- break;
5641
- }
6133
+ let j = i;
6134
+ while (j < lines.length && (lines[j].match(/\t/g) || []).length > 0) {
6135
+ j++;
5642
6136
  }
5643
- if (consecutiveTableLines >= 3) {
5644
- if (inList) {
5645
- html += "</ul>";
5646
- inList = false;
5647
- }
5648
- tableLines = lines.slice(i, j);
5649
- flushTable();
5650
- i = j;
5651
- continue;
6137
+ if (inList) {
6138
+ html += "</ul>";
6139
+ inList = false;
5652
6140
  }
6141
+ tableLines = lines.slice(i, j);
6142
+ flushTable();
6143
+ i = j;
6144
+ continue;
5653
6145
  }
5654
6146
  line = line.trim();
5655
6147
  if (!line) {
@@ -5669,25 +6161,26 @@ var DocPlugin = class {
5669
6161
  i++;
5670
6162
  continue;
5671
6163
  }
6164
+ const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
5672
6165
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
5673
6166
  if (inList) {
5674
6167
  html += "</ul>";
5675
6168
  inList = false;
5676
6169
  }
5677
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line)}</h2>`;
6170
+ html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</h2>`;
5678
6171
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5679
6172
  if (!inList) {
5680
6173
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5681
6174
  inList = true;
5682
6175
  }
5683
6176
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5684
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText)}</li>`;
6177
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
5685
6178
  } else {
5686
6179
  if (inList) {
5687
6180
  html += "</ul>";
5688
6181
  inList = false;
5689
6182
  }
5690
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line)}</p>`;
6183
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
5691
6184
  }
5692
6185
  i++;
5693
6186
  }
@@ -6260,6 +6753,7 @@ exports.excelPlugin = excelPlugin;
6260
6753
  exports.extractExtension = extractExtension;
6261
6754
  exports.formatFileSize = formatFileSize;
6262
6755
  exports.getDefaultPlugins = getDefaultPlugins;
6756
+ exports.getTransferPayload = getTransferPayload;
6263
6757
  exports.htmlPreviewPlugin = htmlPreviewPlugin;
6264
6758
  exports.markdownPlugin = markdownPlugin;
6265
6759
  exports.mediaPlugin = mediaPlugin;
@@ -6272,6 +6766,7 @@ exports.printElement = printElement;
6272
6766
  exports.rtfPlugin = rtfPlugin;
6273
6767
  exports.sanitizeHTML = sanitizeHTML;
6274
6768
  exports.sanitizeSVG = sanitizeSVG;
6769
+ exports.saveTransferPayload = saveTransferPayload;
6275
6770
  exports.sourceToArrayBuffer = sourceToArrayBuffer;
6276
6771
  exports.threeDPlugin = threeDPlugin;
6277
6772
  //# sourceMappingURL=index.cjs.map