@files-preview-app/preview-file 1.3.2 → 1.3.4

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 CHANGED
@@ -3556,26 +3556,6 @@ var ExcelPlugin = class {
3556
3556
  instance.zoomIn?.();
3557
3557
  }
3558
3558
  },
3559
- {
3560
- id: "fit-page",
3561
- icon: "fit-page",
3562
- label: "Fit to View",
3563
- type: "button",
3564
- group: "zoom",
3565
- execute: () => {
3566
- instance.fitToPage?.();
3567
- }
3568
- },
3569
- {
3570
- id: "rotate-cw",
3571
- icon: "rotate-cw",
3572
- label: "Rotate",
3573
- type: "button",
3574
- group: "view",
3575
- execute: () => {
3576
- instance.rotateCW?.();
3577
- }
3578
- },
3579
3559
  {
3580
3560
  id: "download",
3581
3561
  icon: "download",
@@ -3636,12 +3616,11 @@ var ExcelPlugin = class {
3636
3616
  container.appendChild(tabsArea);
3637
3617
  ctx.container.appendChild(container);
3638
3618
  let scale = 1;
3639
- let rotation = 0;
3640
3619
  let currentSheetIndex = 1;
3641
3620
  let sheetNames = [];
3642
3621
  let wb = null;
3643
3622
  const applyTransform = () => {
3644
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3623
+ contentArea.style.transform = `scale(${scale})`;
3645
3624
  contentArea.style.transformOrigin = "top left";
3646
3625
  };
3647
3626
  const calculateFitScale = () => {
@@ -3649,7 +3628,7 @@ var ExcelPlugin = class {
3649
3628
  if (!table) return 1;
3650
3629
  const availW = Math.max(280, container.clientWidth - 48);
3651
3630
  const tW = table.offsetWidth || 800;
3652
- return Math.max(0.65, Math.min(1, availW / tW));
3631
+ return Math.max(0.4, Math.min(1, availW / tW));
3653
3632
  };
3654
3633
  try {
3655
3634
  wb = XLSX__namespace.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3704,7 +3683,16 @@ var ExcelPlugin = class {
3704
3683
  }
3705
3684
  });
3706
3685
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3686
+ requestAnimationFrame(() => {
3687
+ scale = calculateFitScale();
3688
+ applyTransform();
3689
+ });
3707
3690
  };
3691
+ const ro = new ResizeObserver(() => {
3692
+ scale = calculateFitScale();
3693
+ applyTransform();
3694
+ });
3695
+ ro.observe(container);
3708
3696
  if (sheetNames.length > 0) {
3709
3697
  sheetNames.forEach((name, idx) => {
3710
3698
  const btn = document.createElement("button");
@@ -3731,6 +3719,7 @@ var ExcelPlugin = class {
3731
3719
  `;
3732
3720
  }
3733
3721
  const cleanup = () => {
3722
+ ro.disconnect();
3734
3723
  container.remove();
3735
3724
  ctx.container.innerHTML = "";
3736
3725
  };
@@ -3752,16 +3741,11 @@ var ExcelPlugin = class {
3752
3741
  },
3753
3742
  fitToPage: () => {
3754
3743
  scale = calculateFitScale();
3755
- rotation = 0;
3756
3744
  applyTransform();
3757
3745
  },
3758
3746
  rotateCW: () => {
3759
- rotation = (rotation + 90) % 360;
3760
- applyTransform();
3761
3747
  },
3762
3748
  rotateCCW: () => {
3763
- rotation = (rotation - 90 + 360) % 360;
3764
- applyTransform();
3765
3749
  },
3766
3750
  goToPage: (page) => {
3767
3751
  if (page > 0 && page <= sheetNames.length) {
@@ -3859,38 +3843,15 @@ var CsvPlugin = class {
3859
3843
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3860
3844
  }
3861
3845
  getToolbarActions(instance) {
3862
- const totalPages = instance.getPageCount?.() ?? 1;
3863
3846
  const actions = [];
3864
3847
  actions.push({
3865
3848
  id: "thumbnails",
3866
3849
  icon: "thumbnails",
3867
- label: "Page Thumbnails",
3850
+ label: "Sheet Thumbnails",
3868
3851
  type: "button",
3869
3852
  group: "navigation",
3870
3853
  execute: () => instance.toggleThumbnails?.()
3871
3854
  });
3872
- if (totalPages > 1) {
3873
- actions.push({
3874
- id: "page-nav",
3875
- icon: "",
3876
- label: "Page Navigation",
3877
- type: "page-nav",
3878
- group: "navigation",
3879
- value: instance.getCurrentPage?.() ?? 1,
3880
- max: totalPages,
3881
- execute: (action, page) => {
3882
- const cur = instance.getCurrentPage?.() ?? 1;
3883
- const max = instance.getPageCount?.() ?? 1;
3884
- if (action === "prev") {
3885
- if (cur > 1) instance.goToPage?.(cur - 1);
3886
- } else if (action === "next") {
3887
- if (cur < max) instance.goToPage?.(cur + 1);
3888
- } else if (typeof page === "number") {
3889
- instance.goToPage?.(page);
3890
- }
3891
- }
3892
- });
3893
- }
3894
3855
  actions.push(
3895
3856
  {
3896
3857
  id: "zoom-out",
@@ -3912,16 +3873,6 @@ var CsvPlugin = class {
3912
3873
  instance.zoomIn?.();
3913
3874
  }
3914
3875
  },
3915
- {
3916
- id: "fit-page",
3917
- icon: "fit-page",
3918
- label: "Fit to View",
3919
- type: "button",
3920
- group: "zoom",
3921
- execute: () => {
3922
- instance.fitToPage?.();
3923
- }
3924
- },
3925
3876
  {
3926
3877
  id: "download",
3927
3878
  icon: "download",
@@ -3964,131 +3915,135 @@ var CsvPlugin = class {
3964
3915
  container.style.overflow = "auto";
3965
3916
  container.style.padding = "16px";
3966
3917
  container.style.boxSizing = "border-box";
3967
- container.style.transformOrigin = "top left";
3918
+ const tableWrapper = document.createElement("div");
3919
+ tableWrapper.style.transformOrigin = "top left";
3968
3920
  const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3969
3921
  const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3970
3922
  const headerLine = allLines[0] || "";
3971
3923
  const headerCells = headerLine.split(delimiter);
3972
3924
  const dataLines = allLines.slice(1);
3973
- const ROWS_PER_PAGE = 100;
3974
- const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3975
- let currentPage = 1;
3976
3925
  const table = document.createElement("table");
3977
3926
  table.style.borderCollapse = "collapse";
3978
- table.style.width = "100%";
3927
+ table.style.minWidth = "100%";
3979
3928
  table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3980
3929
  table.style.fontSize = "13px";
3981
- const renderPage = (pageNum) => {
3982
- currentPage = Math.max(1, Math.min(totalPages, pageNum));
3983
- table.innerHTML = "";
3984
- const headerTr = document.createElement("tr");
3985
- headerCells.forEach((cell) => {
3986
- const th = document.createElement("th");
3987
- th.textContent = cell.trim();
3988
- th.style.border = "1px solid #d0d7de";
3989
- th.style.padding = "8px 12px";
3990
- th.style.backgroundColor = "#f6f8fa";
3991
- th.style.fontWeight = "600";
3992
- th.style.whiteSpace = "nowrap";
3993
- headerTr.appendChild(th);
3994
- });
3995
- table.appendChild(headerTr);
3996
- const start = (currentPage - 1) * ROWS_PER_PAGE;
3997
- const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
3998
- const pageRows = dataLines.slice(start, end);
3999
- pageRows.forEach((row, idx) => {
4000
- const tr = document.createElement("tr");
4001
- if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4002
- const cells = row.split(delimiter);
4003
- cells.forEach((cell) => {
4004
- const td = document.createElement("td");
4005
- td.textContent = cell.trim();
4006
- td.style.border = "1px solid #d0d7de";
4007
- td.style.padding = "6px 12px";
4008
- td.style.whiteSpace = "nowrap";
4009
- tr.appendChild(td);
4010
- });
4011
- table.appendChild(tr);
3930
+ table.style.background = "#ffffff";
3931
+ table.style.border = "1px solid #d0d7de";
3932
+ const headerTr = document.createElement("tr");
3933
+ headerCells.forEach((cell) => {
3934
+ const th = document.createElement("th");
3935
+ th.textContent = cell.trim();
3936
+ th.style.border = "1px solid #d0d7de";
3937
+ th.style.padding = "8px 12px";
3938
+ th.style.backgroundColor = "#f6f8fa";
3939
+ th.style.fontWeight = "600";
3940
+ th.style.whiteSpace = "nowrap";
3941
+ headerTr.appendChild(th);
3942
+ });
3943
+ table.appendChild(headerTr);
3944
+ dataLines.forEach((row, idx) => {
3945
+ const tr = document.createElement("tr");
3946
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3947
+ const cells = row.split(delimiter);
3948
+ cells.forEach((cell) => {
3949
+ const td = document.createElement("td");
3950
+ td.textContent = cell.trim();
3951
+ td.style.border = "1px solid #d0d7de";
3952
+ td.style.padding = "6px 12px";
3953
+ td.style.whiteSpace = "nowrap";
3954
+ tr.appendChild(td);
4012
3955
  });
4013
- container.scrollTop = 0;
4014
- ctx.emit("page-change", { page: currentPage, total: totalPages });
4015
- };
4016
- renderPage(1);
4017
- container.appendChild(table);
3956
+ table.appendChild(tr);
3957
+ });
3958
+ tableWrapper.appendChild(table);
3959
+ container.appendChild(tableWrapper);
4018
3960
  ctx.container.appendChild(container);
4019
3961
  let scale = 1;
3962
+ const calculateFitScale = () => {
3963
+ const availW = Math.max(280, container.clientWidth - 48);
3964
+ const tW = table.offsetWidth || 800;
3965
+ return Math.max(0.4, Math.min(1, availW / tW));
3966
+ };
4020
3967
  const applyScale = () => {
4021
- container.style.transform = `scale(${scale})`;
4022
- container.style.transformOrigin = "top left";
3968
+ tableWrapper.style.transform = `scale(${scale})`;
4023
3969
  };
3970
+ requestAnimationFrame(() => {
3971
+ scale = calculateFitScale();
3972
+ applyScale();
3973
+ });
3974
+ const ro = new ResizeObserver(() => {
3975
+ scale = calculateFitScale();
3976
+ applyScale();
3977
+ });
3978
+ ro.observe(container);
4024
3979
  const cleanup = () => {
3980
+ ro.disconnect();
4025
3981
  container.remove();
4026
3982
  ctx.container.innerHTML = "";
4027
3983
  };
4028
3984
  ctx.signal.addEventListener("abort", cleanup);
4029
3985
  return {
4030
3986
  destroy: cleanup,
4031
- getPageCount: () => totalPages,
4032
- getCurrentPage: () => currentPage,
4033
- goToPage: (page) => renderPage(page),
3987
+ getPageCount: () => 1,
3988
+ getCurrentPage: () => 1,
3989
+ goToPage: () => {
3990
+ },
4034
3991
  getThumbnails: () => {
4035
- const thumbnails = [];
4036
- for (let i = 0; i < totalPages; i++) {
4037
- const pageIdx = i;
4038
- const startRow = pageIdx * ROWS_PER_PAGE + 1;
4039
- const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4040
- const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4041
- thumbnails.push({
4042
- index: pageIdx,
4043
- label,
4044
- render: async (canvas) => {
4045
- canvas.width = 140;
4046
- canvas.height = 100;
4047
- const c = canvas.getContext("2d");
4048
- if (!c) return;
4049
- c.fillStyle = "#ffffff";
4050
- c.fillRect(0, 0, canvas.width, canvas.height);
4051
- c.fillStyle = "#2563eb";
4052
- c.fillRect(0, 0, canvas.width, 16);
4053
- c.fillStyle = "#ffffff";
4054
- c.font = "bold 8px sans-serif";
4055
- c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4056
- const startY = 17;
4057
- const rowH = 13;
4058
- const colW = 28;
4059
- const cols = Math.min(4, headerCells.length || 4);
4060
- c.fillStyle = "#f1f5f9";
4061
- c.fillRect(0, startY, canvas.width, rowH);
4062
- c.strokeStyle = "#cbd5e1";
4063
- c.lineWidth = 0.8;
4064
- c.strokeRect(0, startY, canvas.width, rowH);
4065
- c.fillStyle = "#64748b";
4066
- c.font = "bold 7px sans-serif";
4067
- for (let ci = 0; ci < cols; ci++) {
4068
- const hName = headerCells[ci] || `Col ${ci + 1}`;
4069
- c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4070
- }
4071
- const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4072
- for (let ri = 0; ri < rowsToPreview.length; ri++) {
4073
- const y = startY + (ri + 1) * rowH;
4074
- if (y > canvas.height - 2) break;
4075
- c.strokeStyle = "#e2e8f0";
4076
- c.strokeRect(0, y, canvas.width, rowH);
4077
- const cVals = rowsToPreview[ri].split(delimiter);
4078
- c.fillStyle = "#334155";
4079
- c.font = "6px sans-serif";
4080
- for (let ci = 0; ci < cols; ci++) {
4081
- const val = cVals[ci] || "";
4082
- c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
3992
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3993
+ return [{
3994
+ index: 0,
3995
+ label: sheetName,
3996
+ render: async (canvas) => {
3997
+ canvas.width = 140;
3998
+ canvas.height = 100;
3999
+ const c = canvas.getContext("2d");
4000
+ if (!c) return;
4001
+ c.fillStyle = "#ffffff";
4002
+ c.fillRect(0, 0, canvas.width, canvas.height);
4003
+ c.fillStyle = "#107c41";
4004
+ c.fillRect(0, 0, canvas.width, 16);
4005
+ c.fillStyle = "#ffffff";
4006
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
4007
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
4008
+ const startY = 17;
4009
+ const rowH = 13;
4010
+ const colW = 30;
4011
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
4012
+ c.fillStyle = "#f1f5f9";
4013
+ c.fillRect(0, startY, canvas.width, rowH);
4014
+ c.strokeStyle = "#cbd5e1";
4015
+ c.lineWidth = 0.8;
4016
+ c.strokeRect(0, startY, canvas.width, rowH);
4017
+ c.fillStyle = "#64748b";
4018
+ c.font = "bold 7px sans-serif";
4019
+ for (let ci = 0; ci < cols.length; ci++) {
4020
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
4021
+ }
4022
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
4023
+ const y = startY + (ri + 1) * rowH;
4024
+ if (y > canvas.height - 2) break;
4025
+ c.fillStyle = "#f8fafc";
4026
+ c.fillRect(0, y, 12, rowH);
4027
+ c.fillStyle = "#94a3b8";
4028
+ c.font = "6px sans-serif";
4029
+ c.fillText(String(ri + 1), 3, y + 9);
4030
+ c.strokeStyle = "#e2e8f0";
4031
+ c.strokeRect(0, y, canvas.width, rowH);
4032
+ const rowCells = dataLines[ri].split(delimiter);
4033
+ c.fillStyle = "#334155";
4034
+ c.font = "6px sans-serif";
4035
+ for (let ci = 0; ci < cols.length; ci++) {
4036
+ const val = (rowCells[ci] || "").trim();
4037
+ if (val) {
4038
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4083
4039
  }
4084
4040
  }
4085
- c.strokeStyle = "#cbd5e1";
4086
- c.lineWidth = 1;
4087
- c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4088
4041
  }
4089
- });
4090
- }
4091
- return thumbnails;
4042
+ c.strokeStyle = "#cbd5e1";
4043
+ c.lineWidth = 1;
4044
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4045
+ }
4046
+ }];
4092
4047
  },
4093
4048
  zoomIn: () => {
4094
4049
  scale += 0.1;
@@ -4104,11 +4059,13 @@ var CsvPlugin = class {
4104
4059
  applyScale();
4105
4060
  },
4106
4061
  fitToPage: () => {
4107
- const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4108
- const tW = table.offsetWidth || 600;
4109
- scale = Math.max(0.35, Math.min(1, availW / tW));
4062
+ scale = calculateFitScale();
4110
4063
  applyScale();
4111
4064
  },
4065
+ rotateCW: () => {
4066
+ },
4067
+ rotateCCW: () => {
4068
+ },
4112
4069
  download: () => {
4113
4070
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
4114
4071
  const url = URL.createObjectURL(blob);
@@ -4220,26 +4177,6 @@ var CodePlugin = class {
4220
4177
  instance.zoomIn?.();
4221
4178
  }
4222
4179
  },
4223
- {
4224
- id: "fit-page",
4225
- icon: "fit-page",
4226
- label: "Fit to View",
4227
- type: "button",
4228
- group: "zoom",
4229
- execute: () => {
4230
- instance.fitToPage?.();
4231
- }
4232
- },
4233
- {
4234
- id: "rotate-cw",
4235
- icon: "rotate-cw",
4236
- label: "Rotate",
4237
- type: "button",
4238
- group: "view",
4239
- execute: () => {
4240
- instance.rotateCW?.();
4241
- }
4242
- },
4243
4180
  {
4244
4181
  id: "copy",
4245
4182
  icon: "copy",
@@ -5383,6 +5320,166 @@ var ThreeDPlugin = class {
5383
5320
  function threeDPlugin() {
5384
5321
  return new ThreeDPlugin();
5385
5322
  }
5323
+ function cleanRtfText(raw) {
5324
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5325
+ let num = parseInt(code, 10);
5326
+ if (num < 0) num += 65536;
5327
+ return String.fromCharCode(num);
5328
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5329
+ return String.fromCharCode(parseInt(hex, 16));
5330
+ }).replace(/\\tab\b/g, " ").replace(/\\emdash\b/g, "\u2014").replace(/\\endash\b/g, "\u2013").replace(/\\bullet\b/g, "\u2022").replace(/\\lquote\b/g, "\u2018").replace(/\\rquote\b/g, "\u2019").replace(/\\ldblquote\b/g, "\u201C").replace(/\\rdblquote\b/g, "\u201D").replace(/\\\w+(?:-?\d+)?\s?/g, " ").replace(/[{}\r\n]/g, " ").replace(/\s+/g, " ").trim();
5331
+ }
5332
+ function parseRtfTables(rtfText) {
5333
+ const rowSegments = rtfText.split(/\\row\b/);
5334
+ if (rowSegments.length <= 1) return [];
5335
+ const rawTables = [];
5336
+ let currentTable = null;
5337
+ let charOffset = 0;
5338
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5339
+ const segment = rowSegments[i];
5340
+ const segmentStart = charOffset;
5341
+ const segmentEnd = charOffset + segment.length + 4;
5342
+ const cellParts = segment.split(/\\cell\b/);
5343
+ if (cellParts.length > 1) {
5344
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5345
+ let cellWidths = [];
5346
+ if (cellxMatches.length > 0) {
5347
+ let seq = [cellxMatches[0]];
5348
+ for (let k = 1; k < cellxMatches.length; k++) {
5349
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5350
+ else break;
5351
+ }
5352
+ let prev = 0;
5353
+ cellWidths = seq.map((x) => {
5354
+ const w = x - prev;
5355
+ prev = x;
5356
+ return w;
5357
+ });
5358
+ }
5359
+ const cells = [];
5360
+ for (let c = 0; c < cellParts.length - 1; c++) {
5361
+ let rawCell = cellParts[c];
5362
+ if (c === 0) {
5363
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5364
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5365
+ }
5366
+ cells.push(cleanRtfText(rawCell));
5367
+ }
5368
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5369
+ if (isNewTable) {
5370
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5371
+ const cleanPrev = cleanRtfText(prevTextSlice);
5372
+ const anchorBefore = cleanPrev.slice(-80).trim();
5373
+ currentTable = {
5374
+ id: `RTF_TABLE_${rawTables.length}`,
5375
+ startPos: segmentStart,
5376
+ lastEnd: segmentEnd,
5377
+ rows: [],
5378
+ columnWidths: cellWidths,
5379
+ anchorBefore,
5380
+ anchorAfter: ""
5381
+ };
5382
+ rawTables.push(currentTable);
5383
+ }
5384
+ if (currentTable) {
5385
+ currentTable.rows.push({
5386
+ cells,
5387
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5388
+ });
5389
+ currentTable.lastEnd = segmentEnd;
5390
+ }
5391
+ }
5392
+ charOffset += segment.length + 4;
5393
+ }
5394
+ rawTables.forEach((tbl) => {
5395
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5396
+ const cleanAfter = cleanRtfText(afterSlice);
5397
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5398
+ });
5399
+ return rawTables.map((tbl) => {
5400
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5401
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5402
+ return {
5403
+ id: tbl.id,
5404
+ rows: tbl.rows.map((r) => r.cells),
5405
+ colPercents,
5406
+ anchorBefore: tbl.anchorBefore,
5407
+ anchorAfter: tbl.anchorAfter
5408
+ };
5409
+ });
5410
+ }
5411
+ function renderRtfTableElement(table) {
5412
+ const tableEl = document.createElement("table");
5413
+ tableEl.className = "fp-rtf-table";
5414
+ tableEl.style.width = "100%";
5415
+ tableEl.style.borderCollapse = "collapse";
5416
+ tableEl.style.margin = "16px 0 20px";
5417
+ tableEl.style.fontSize = "10pt";
5418
+ tableEl.style.lineHeight = "1.5";
5419
+ tableEl.style.border = "1px solid #cbd5e1";
5420
+ tableEl.style.borderRadius = "4px";
5421
+ tableEl.style.boxSizing = "border-box";
5422
+ tableEl.style.backgroundColor = "#ffffff";
5423
+ const thead = document.createElement("thead");
5424
+ const tbody = document.createElement("tbody");
5425
+ table.rows.forEach((rowCells, rIdx) => {
5426
+ const tr = document.createElement("tr");
5427
+ const isHeader = rIdx === 0;
5428
+ if (isHeader) {
5429
+ tr.style.backgroundColor = "#f8fafc";
5430
+ tr.style.borderBottom = "2px solid #cbd5e1";
5431
+ } else {
5432
+ tr.style.borderBottom = "1px solid #e2e8f0";
5433
+ if (rIdx % 2 === 0) {
5434
+ tr.style.backgroundColor = "#fcfdfe";
5435
+ }
5436
+ }
5437
+ rowCells.forEach((cellText, cIdx) => {
5438
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5439
+ cellEl.style.padding = "8px 12px";
5440
+ cellEl.style.border = "1px solid #cbd5e1";
5441
+ cellEl.style.verticalAlign = "top";
5442
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5443
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5444
+ if (table.colPercents && table.colPercents[cIdx]) {
5445
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5446
+ }
5447
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5448
+ cellEl.style.textAlign = "center";
5449
+ } else {
5450
+ cellEl.style.textAlign = "left";
5451
+ }
5452
+ cellEl.textContent = cellText;
5453
+ tr.appendChild(cellEl);
5454
+ });
5455
+ if (isHeader) {
5456
+ thead.appendChild(tr);
5457
+ } else {
5458
+ tbody.appendChild(tr);
5459
+ }
5460
+ });
5461
+ tableEl.appendChild(thead);
5462
+ tableEl.appendChild(tbody);
5463
+ return tableEl;
5464
+ }
5465
+ function extractMediaFingerprint(el) {
5466
+ const img = el.tagName.toLowerCase() === "img" ? el : el.querySelector("img");
5467
+ if (img && img.src) {
5468
+ const s = img.src;
5469
+ return s.length > 300 ? `img_${s.slice(0, 80)}_${s.length}_${s.slice(-80)}` : `img_${s}`;
5470
+ }
5471
+ const innerImg = el.querySelector("image");
5472
+ if (innerImg) {
5473
+ const h = innerImg.getAttribute("xlink:href") || innerImg.getAttribute("href") || "";
5474
+ if (h) return h.length > 300 ? `svgimg_${h.slice(0, 80)}_${h.length}_${h.slice(-80)}` : `svgimg_${h}`;
5475
+ }
5476
+ const svg = el.tagName.toLowerCase() === "svg" ? el : el.querySelector("svg");
5477
+ if (svg) {
5478
+ const vb = svg.getAttribute("viewBox") || "";
5479
+ return `svg_${vb}_${svg.innerHTML.length}`;
5480
+ }
5481
+ return null;
5482
+ }
5386
5483
  var RtfPlugin = class {
5387
5484
  id = "rtf";
5388
5485
  name = "Rich Text Format (RTF)";
@@ -5418,6 +5515,14 @@ var RtfPlugin = class {
5418
5515
  }
5419
5516
  });
5420
5517
  actions.push(
5518
+ {
5519
+ id: "thumbnails",
5520
+ icon: "thumbnails",
5521
+ label: "Thumbnails",
5522
+ type: "button",
5523
+ group: "view",
5524
+ execute: () => instance.toggleThumbnails?.()
5525
+ },
5421
5526
  {
5422
5527
  id: "zoom-out",
5423
5528
  icon: "zoom-out",
@@ -5545,9 +5650,29 @@ var RtfPlugin = class {
5545
5650
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
5546
5651
  RTFJS__namespace.loggingEnabled(false);
5547
5652
  }
5548
- const doc = new RTFJS__namespace.Document(ctx.buffer, {});
5653
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5654
+ const tables = parseRtfTables(rawText);
5655
+ const seenMediaSignatures = /* @__PURE__ */ new Set();
5656
+ const doc = new RTFJS__namespace.Document(ctx.buffer, {
5657
+ onPicture: (isLegacy, createPic) => {
5658
+ if (isLegacy === true) {
5659
+ return null;
5660
+ }
5661
+ const pic = createPic();
5662
+ if (!pic) return null;
5663
+ const fp = extractMediaFingerprint(pic);
5664
+ if (fp) {
5665
+ if (seenMediaSignatures.has(fp)) {
5666
+ return null;
5667
+ }
5668
+ seenMediaSignatures.add(fp);
5669
+ }
5670
+ return pic;
5671
+ }
5672
+ });
5549
5673
  const htmlElements = await doc.render();
5550
5674
  const contentNodes = [];
5675
+ const seenInContentBlocks = /* @__PURE__ */ new Set();
5551
5676
  const extractBlocks = (nodes) => {
5552
5677
  for (const item of nodes) {
5553
5678
  if (!item || !(item instanceof HTMLElement)) continue;
@@ -5559,6 +5684,32 @@ var RtfPlugin = class {
5559
5684
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5560
5685
  extractBlocks(Array.from(item.children));
5561
5686
  } else {
5687
+ const fp = extractMediaFingerprint(item);
5688
+ if (fp) {
5689
+ if (seenInContentBlocks.has(fp)) {
5690
+ continue;
5691
+ }
5692
+ seenInContentBlocks.add(fp);
5693
+ }
5694
+ const hasMedia = item.querySelector("img, svg, canvas, table") !== null || ["img", "svg", "table"].includes(tag);
5695
+ const textContent = (item.textContent || "").trim();
5696
+ if (!hasMedia && textContent.length === 0) {
5697
+ continue;
5698
+ }
5699
+ const svgs = item.querySelectorAll("svg, img");
5700
+ svgs.forEach((s) => {
5701
+ const el = s;
5702
+ el.style.maxWidth = "100%";
5703
+ el.style.maxHeight = "360px";
5704
+ el.style.display = "block";
5705
+ el.style.margin = "12px auto";
5706
+ if (s.tagName.toLowerCase() === "svg") {
5707
+ s.removeAttribute("width");
5708
+ s.removeAttribute("height");
5709
+ el.style.width = "100%";
5710
+ el.style.maxHeight = "360px";
5711
+ }
5712
+ });
5562
5713
  if (tag === "p" || tag === "div") {
5563
5714
  item.style.display = "block";
5564
5715
  item.style.marginBottom = "12px";
@@ -5569,52 +5720,120 @@ var RtfPlugin = class {
5569
5720
  }
5570
5721
  };
5571
5722
  extractBlocks(htmlElements);
5572
- wrapper.innerHTML = "";
5573
- contentNodes.forEach((node) => wrapper.appendChild(node));
5574
- const childHeights = contentNodes.map((c) => {
5575
- const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5576
- const offH = c.offsetHeight || 0;
5577
- const textLen = c.textContent?.trim().length || 0;
5578
- const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
5579
- return Math.max(rectH, offH, estH);
5723
+ tables.forEach((table) => {
5724
+ const tableEl = renderRtfTableElement(table);
5725
+ let inserted = false;
5726
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5727
+ const keyword = table.anchorBefore.slice(-35).trim();
5728
+ for (let i = 0; i < contentNodes.length; i++) {
5729
+ if (contentNodes[i].textContent?.includes(keyword)) {
5730
+ contentNodes.splice(i + 1, 0, tableEl);
5731
+ inserted = true;
5732
+ break;
5733
+ }
5734
+ }
5735
+ }
5736
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5737
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5738
+ for (let i = 0; i < contentNodes.length; i++) {
5739
+ if (contentNodes[i].textContent?.includes(keyword)) {
5740
+ contentNodes.splice(i, 0, tableEl);
5741
+ inserted = true;
5742
+ break;
5743
+ }
5744
+ }
5745
+ }
5746
+ if (!inserted) {
5747
+ if (contentNodes.length > 2) {
5748
+ contentNodes.splice(2, 0, tableEl);
5749
+ } else {
5750
+ contentNodes.push(tableEl);
5751
+ }
5752
+ }
5580
5753
  });
5581
- wrapper.innerHTML = "";
5582
- const createRtfCard = () => {
5754
+ const createRtfCard = (pageNum) => {
5583
5755
  const card = document.createElement("div");
5584
5756
  card.className = "fp-rtf-page-card";
5757
+ card.setAttribute("data-page-number", String(pageNum));
5585
5758
  card.style.backgroundColor = "#ffffff";
5586
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5759
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.04)";
5587
5760
  card.style.borderRadius = "4px";
5588
- card.style.padding = "72px 56px";
5761
+ card.style.padding = "64px 56px";
5589
5762
  card.style.width = "816px";
5590
5763
  card.style.minHeight = "1056px";
5764
+ card.style.maxHeight = "1056px";
5765
+ card.style.overflow = "hidden";
5591
5766
  card.style.boxSizing = "border-box";
5592
5767
  card.style.marginBottom = "24px";
5593
5768
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5594
5769
  card.style.lineHeight = "1.6";
5595
5770
  card.style.color = "#1e293b";
5771
+ card.style.position = "relative";
5596
5772
  return card;
5597
5773
  };
5598
- let curCard = createRtfCard();
5774
+ wrapper.innerHTML = "";
5775
+ const measureCard = createRtfCard(0);
5776
+ measureCard.style.maxHeight = "none";
5777
+ measureCard.style.height = "auto";
5778
+ wrapper.appendChild(measureCard);
5779
+ contentNodes.forEach((node) => measureCard.appendChild(node));
5780
+ const childHeights = contentNodes.map((c) => {
5781
+ const rect = c.getBoundingClientRect ? c.getBoundingClientRect() : null;
5782
+ const rectH = rect ? rect.height : 0;
5783
+ const offH = c.offsetHeight || 0;
5784
+ let realH = Math.max(rectH, offH);
5785
+ try {
5786
+ const cs = window.getComputedStyle(c);
5787
+ realH += (parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0);
5788
+ } catch {
5789
+ }
5790
+ const hasMedia = c.querySelector("img, svg") !== null || ["img", "svg"].includes(c.tagName.toLowerCase());
5791
+ const hasTable = c.querySelector("table") !== null || c.tagName.toLowerCase() === "table";
5792
+ if (hasMedia) {
5793
+ realH = Math.max(realH, 300);
5794
+ } else if (hasTable) {
5795
+ realH = Math.max(realH, 240);
5796
+ }
5797
+ if (realH > 0) return realH;
5798
+ const textLen = c.textContent?.trim().length || 0;
5799
+ return Math.max(28, Math.ceil(textLen / 75) * 26 + 16);
5800
+ });
5801
+ wrapper.innerHTML = "";
5802
+ let curCard = createRtfCard(1);
5599
5803
  wrapper.appendChild(curCard);
5600
5804
  pageElements = [curCard];
5601
5805
  let curH = 0;
5602
- const maxH = 912;
5806
+ const maxH = 860;
5603
5807
  for (let i = 0; i < contentNodes.length; i++) {
5604
5808
  const child = contentNodes[i];
5605
5809
  const chH = childHeights[i];
5606
- curCard.appendChild(child);
5607
- curH += chH;
5608
- if (curH >= maxH && i < contentNodes.length - 1) {
5609
- curCard = createRtfCard();
5810
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5811
+ if (curH === 0 && isChildEmpty) {
5812
+ continue;
5813
+ }
5814
+ const isTable = child.querySelector("table") !== null || child.tagName.toLowerCase() === "table";
5815
+ const shouldBreakBeforeTable = isTable && curH > 400 && curCard.childNodes.length > 0;
5816
+ if ((curH + chH > maxH || shouldBreakBeforeTable) && curCard.childNodes.length > 0) {
5817
+ curCard = createRtfCard(pageElements.length + 1);
5610
5818
  wrapper.appendChild(curCard);
5611
5819
  pageElements.push(curCard);
5612
5820
  curH = 0;
5613
5821
  }
5822
+ curCard.appendChild(child);
5823
+ curH += chH;
5824
+ }
5825
+ pageElements = pageElements.filter((card) => {
5826
+ const hasText = (card.textContent || "").trim().length > 0;
5827
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5828
+ return hasText || hasMedia;
5829
+ });
5830
+ if (pageElements.length === 0) {
5831
+ pageElements = [curCard];
5614
5832
  }
5615
5833
  } catch (err) {
5616
5834
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5617
5835
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5836
+ const tables = parseRtfTables(text);
5618
5837
  const rawPages = text.split(/\\page\b/).map((segment) => {
5619
5838
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5620
5839
  }).filter((p) => p.length > 0);
@@ -5625,7 +5844,7 @@ var RtfPlugin = class {
5625
5844
  pageCard.style.backgroundColor = "#ffffff";
5626
5845
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5627
5846
  pageCard.style.borderRadius = "4px";
5628
- pageCard.style.padding = "72px 56px";
5847
+ pageCard.style.padding = "64px 56px";
5629
5848
  pageCard.style.width = "816px";
5630
5849
  pageCard.style.minHeight = "1056px";
5631
5850
  pageCard.style.boxSizing = "border-box";
@@ -5636,6 +5855,9 @@ var RtfPlugin = class {
5636
5855
  pageCard.style.whiteSpace = "pre-wrap";
5637
5856
  pageCard.style.display = i === 0 ? "block" : "none";
5638
5857
  pageCard.textContent = pages[i];
5858
+ if (i === 1 && tables.length > 0) {
5859
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5860
+ }
5639
5861
  wrapper.appendChild(pageCard);
5640
5862
  pageElements.push(pageCard);
5641
5863
  }
@@ -5666,6 +5888,32 @@ var RtfPlugin = class {
5666
5888
  getPageCount: () => totalPages,
5667
5889
  getCurrentPage: () => currentPage,
5668
5890
  goToPage: (page) => showPage(page),
5891
+ getThumbnails: async () => {
5892
+ const items = [];
5893
+ for (let idx = 0; idx < totalPages; idx++) {
5894
+ const pageIdx = idx;
5895
+ items.push({
5896
+ index: pageIdx,
5897
+ label: `Page ${pageIdx + 1}`,
5898
+ render: async (canvas) => {
5899
+ const ctx2d = canvas.getContext("2d");
5900
+ if (!ctx2d) return;
5901
+ canvas.width = 140;
5902
+ canvas.height = 180;
5903
+ ctx2d.fillStyle = "#ffffff";
5904
+ ctx2d.fillRect(0, 0, 140, 180);
5905
+ ctx2d.strokeStyle = "#cbd5e1";
5906
+ ctx2d.lineWidth = 1;
5907
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5908
+ ctx2d.fillStyle = "#334155";
5909
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5910
+ ctx2d.textAlign = "center";
5911
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5912
+ }
5913
+ });
5914
+ }
5915
+ return items;
5916
+ },
5669
5917
  zoomIn: () => {
5670
5918
  isUserZoomed = true;
5671
5919
  scale += 0.15;
@@ -7695,14 +7943,14 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
7695
7943
  };
7696
7944
 
7697
7945
  // src/angular.ts
7698
- var _container_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
7946
+ var _container_dec, _destroyed_dec, _rotate_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
7699
7947
  _FilePreviewComponent_decorators = [core.Component({
7700
7948
  selector: "fp-file-preview",
7701
7949
  standalone: true,
7702
7950
  imports: [common.CommonModule],
7703
7951
  template: `<div #container [style.width]="'100%'" [style.height]="'100%'"></div>`,
7704
7952
  changeDetection: core.ChangeDetectionStrategy.OnPush
7705
- })], _src_dec = [core.Input({ required: true })], _plugins_dec = [core.Input()], _options_dec = [core.Input()], _loading_dec = [core.Output()], _loaded_dec = [core.Output()], _error_dec = [core.Output()], _pageChange_dec = [core.Output()], _zoomChange_dec = [core.Output()], _container_dec = [core.ViewChild("container", { static: true })];
7953
+ })], _src_dec = [core.Input({ required: true })], _plugins_dec = [core.Input()], _options_dec = [core.Input()], _loading_dec = [core.Output()], _loaded_dec = [core.Output()], _error_dec = [core.Output()], _pageChange_dec = [core.Output()], _zoomChange_dec = [core.Output()], _rotate_dec = [core.Output()], _destroyed_dec = [core.Output()], _container_dec = [core.ViewChild("container", { static: true })];
7706
7954
  exports.FilePreviewComponent = class FilePreviewComponent {
7707
7955
  constructor(ngZone) {
7708
7956
  this.ngZone = ngZone;
@@ -7714,7 +7962,9 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7714
7962
  __publicField(this, "error", __runInitializers(_init, 28, this, new core.EventEmitter())), __runInitializers(_init, 31, this);
7715
7963
  __publicField(this, "pageChange", __runInitializers(_init, 32, this, new core.EventEmitter())), __runInitializers(_init, 35, this);
7716
7964
  __publicField(this, "zoomChange", __runInitializers(_init, 36, this, new core.EventEmitter())), __runInitializers(_init, 39, this);
7717
- __publicField(this, "container", __runInitializers(_init, 40, this)), __runInitializers(_init, 43, this);
7965
+ __publicField(this, "rotate", __runInitializers(_init, 40, this, new core.EventEmitter())), __runInitializers(_init, 43, this);
7966
+ __publicField(this, "destroyed", __runInitializers(_init, 44, this, new core.EventEmitter())), __runInitializers(_init, 47, this);
7967
+ __publicField(this, "container", __runInitializers(_init, 48, this)), __runInitializers(_init, 51, this);
7718
7968
  __publicField(this, "viewer", null);
7719
7969
  __publicField(this, "instance", null);
7720
7970
  }
@@ -7739,6 +7989,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7739
7989
  viewer.on("zoom-change", (data) => {
7740
7990
  this.ngZone.run(() => this.zoomChange.emit(data));
7741
7991
  });
7992
+ viewer.on("rotate", (data) => {
7993
+ this.ngZone.run(() => this.rotate.emit(data));
7994
+ });
7995
+ viewer.on("destroy", () => {
7996
+ this.ngZone.run(() => this.destroyed.emit());
7997
+ });
7742
7998
  this.render();
7743
7999
  }
7744
8000
  ngOnChanges(changes) {
@@ -7773,6 +8029,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7773
8029
  getInstance() {
7774
8030
  return this.instance;
7775
8031
  }
8032
+ getViewer() {
8033
+ return this.viewer;
8034
+ }
8035
+ destroy() {
8036
+ this.viewer?.destroy();
8037
+ }
7776
8038
  };
7777
8039
  _init = __decoratorStart();
7778
8040
  __decorateElement(_init, 5, "src", _src_dec, exports.FilePreviewComponent);
@@ -7783,6 +8045,8 @@ __decorateElement(_init, 5, "loaded", _loaded_dec, exports.FilePreviewComponent)
7783
8045
  __decorateElement(_init, 5, "error", _error_dec, exports.FilePreviewComponent);
7784
8046
  __decorateElement(_init, 5, "pageChange", _pageChange_dec, exports.FilePreviewComponent);
7785
8047
  __decorateElement(_init, 5, "zoomChange", _zoomChange_dec, exports.FilePreviewComponent);
8048
+ __decorateElement(_init, 5, "rotate", _rotate_dec, exports.FilePreviewComponent);
8049
+ __decorateElement(_init, 5, "destroyed", _destroyed_dec, exports.FilePreviewComponent);
7786
8050
  __decorateElement(_init, 5, "container", _container_dec, exports.FilePreviewComponent);
7787
8051
  exports.FilePreviewComponent = __decorateElement(_init, 0, "FilePreviewComponent", _FilePreviewComponent_decorators, exports.FilePreviewComponent);
7788
8052
  __runInitializers(_init, 1, exports.FilePreviewComponent);