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

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,148 @@ 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
+ }
5386
5465
  var RtfPlugin = class {
5387
5466
  id = "rtf";
5388
5467
  name = "Rich Text Format (RTF)";
@@ -5418,6 +5497,14 @@ var RtfPlugin = class {
5418
5497
  }
5419
5498
  });
5420
5499
  actions.push(
5500
+ {
5501
+ id: "thumbnails",
5502
+ icon: "thumbnails",
5503
+ label: "Thumbnails",
5504
+ type: "button",
5505
+ group: "view",
5506
+ execute: () => instance.toggleThumbnails?.()
5507
+ },
5421
5508
  {
5422
5509
  id: "zoom-out",
5423
5510
  icon: "zoom-out",
@@ -5545,6 +5632,8 @@ var RtfPlugin = class {
5545
5632
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
5546
5633
  RTFJS__namespace.loggingEnabled(false);
5547
5634
  }
5635
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5636
+ const tables = parseRtfTables(rawText);
5548
5637
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
5549
5638
  const htmlElements = await doc.render();
5550
5639
  const contentNodes = [];
@@ -5559,6 +5648,20 @@ var RtfPlugin = class {
5559
5648
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5560
5649
  extractBlocks(Array.from(item.children));
5561
5650
  } else {
5651
+ const svgs = item.querySelectorAll("svg, img");
5652
+ svgs.forEach((s) => {
5653
+ const el = s;
5654
+ el.style.maxWidth = "100%";
5655
+ el.style.maxHeight = "360px";
5656
+ el.style.display = "block";
5657
+ el.style.margin = "12px auto";
5658
+ if (s.tagName.toLowerCase() === "svg") {
5659
+ s.removeAttribute("width");
5660
+ s.removeAttribute("height");
5661
+ el.style.width = "100%";
5662
+ el.style.maxHeight = "360px";
5663
+ }
5664
+ });
5562
5665
  if (tag === "p" || tag === "div") {
5563
5666
  item.style.display = "block";
5564
5667
  item.style.marginBottom = "12px";
@@ -5569,14 +5672,46 @@ var RtfPlugin = class {
5569
5672
  }
5570
5673
  };
5571
5674
  extractBlocks(htmlElements);
5675
+ tables.forEach((table) => {
5676
+ const tableEl = renderRtfTableElement(table);
5677
+ let inserted = false;
5678
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5679
+ const keyword = table.anchorBefore.slice(-35).trim();
5680
+ for (let i = 0; i < contentNodes.length; i++) {
5681
+ if (contentNodes[i].textContent?.includes(keyword)) {
5682
+ contentNodes.splice(i + 1, 0, tableEl);
5683
+ inserted = true;
5684
+ break;
5685
+ }
5686
+ }
5687
+ }
5688
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5689
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5690
+ for (let i = 0; i < contentNodes.length; i++) {
5691
+ if (contentNodes[i].textContent?.includes(keyword)) {
5692
+ contentNodes.splice(i, 0, tableEl);
5693
+ inserted = true;
5694
+ break;
5695
+ }
5696
+ }
5697
+ }
5698
+ if (!inserted) {
5699
+ if (contentNodes.length > 2) {
5700
+ contentNodes.splice(2, 0, tableEl);
5701
+ } else {
5702
+ contentNodes.push(tableEl);
5703
+ }
5704
+ }
5705
+ });
5572
5706
  wrapper.innerHTML = "";
5573
5707
  contentNodes.forEach((node) => wrapper.appendChild(node));
5574
5708
  const childHeights = contentNodes.map((c) => {
5575
5709
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5576
5710
  const offH = c.offsetHeight || 0;
5711
+ const realH = Math.max(rectH, offH);
5712
+ if (realH > 0) return realH;
5577
5713
  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);
5714
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
5580
5715
  });
5581
5716
  wrapper.innerHTML = "";
5582
5717
  const createRtfCard = () => {
@@ -5585,7 +5720,7 @@ var RtfPlugin = class {
5585
5720
  card.style.backgroundColor = "#ffffff";
5586
5721
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5587
5722
  card.style.borderRadius = "4px";
5588
- card.style.padding = "72px 56px";
5723
+ card.style.padding = "64px 56px";
5589
5724
  card.style.width = "816px";
5590
5725
  card.style.minHeight = "1056px";
5591
5726
  card.style.boxSizing = "border-box";
@@ -5599,22 +5734,35 @@ var RtfPlugin = class {
5599
5734
  wrapper.appendChild(curCard);
5600
5735
  pageElements = [curCard];
5601
5736
  let curH = 0;
5602
- const maxH = 912;
5737
+ const maxH = 928;
5603
5738
  for (let i = 0; i < contentNodes.length; i++) {
5604
5739
  const child = contentNodes[i];
5605
5740
  const chH = childHeights[i];
5606
- curCard.appendChild(child);
5607
- curH += chH;
5608
- if (curH >= maxH && i < contentNodes.length - 1) {
5741
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5742
+ if (curH === 0 && isChildEmpty) {
5743
+ continue;
5744
+ }
5745
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
5609
5746
  curCard = createRtfCard();
5610
5747
  wrapper.appendChild(curCard);
5611
5748
  pageElements.push(curCard);
5612
5749
  curH = 0;
5613
5750
  }
5751
+ curCard.appendChild(child);
5752
+ curH += chH;
5753
+ }
5754
+ pageElements = pageElements.filter((card) => {
5755
+ const hasText = (card.textContent || "").trim().length > 0;
5756
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5757
+ return hasText || hasMedia;
5758
+ });
5759
+ if (pageElements.length === 0) {
5760
+ pageElements = [curCard];
5614
5761
  }
5615
5762
  } catch (err) {
5616
5763
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5617
5764
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5765
+ const tables = parseRtfTables(text);
5618
5766
  const rawPages = text.split(/\\page\b/).map((segment) => {
5619
5767
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5620
5768
  }).filter((p) => p.length > 0);
@@ -5625,7 +5773,7 @@ var RtfPlugin = class {
5625
5773
  pageCard.style.backgroundColor = "#ffffff";
5626
5774
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5627
5775
  pageCard.style.borderRadius = "4px";
5628
- pageCard.style.padding = "72px 56px";
5776
+ pageCard.style.padding = "64px 56px";
5629
5777
  pageCard.style.width = "816px";
5630
5778
  pageCard.style.minHeight = "1056px";
5631
5779
  pageCard.style.boxSizing = "border-box";
@@ -5636,6 +5784,9 @@ var RtfPlugin = class {
5636
5784
  pageCard.style.whiteSpace = "pre-wrap";
5637
5785
  pageCard.style.display = i === 0 ? "block" : "none";
5638
5786
  pageCard.textContent = pages[i];
5787
+ if (i === 1 && tables.length > 0) {
5788
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5789
+ }
5639
5790
  wrapper.appendChild(pageCard);
5640
5791
  pageElements.push(pageCard);
5641
5792
  }
@@ -5666,6 +5817,32 @@ var RtfPlugin = class {
5666
5817
  getPageCount: () => totalPages,
5667
5818
  getCurrentPage: () => currentPage,
5668
5819
  goToPage: (page) => showPage(page),
5820
+ getThumbnails: async () => {
5821
+ const items = [];
5822
+ for (let idx = 0; idx < totalPages; idx++) {
5823
+ const pageIdx = idx;
5824
+ items.push({
5825
+ index: pageIdx,
5826
+ label: `Page ${pageIdx + 1}`,
5827
+ render: async (canvas) => {
5828
+ const ctx2d = canvas.getContext("2d");
5829
+ if (!ctx2d) return;
5830
+ canvas.width = 140;
5831
+ canvas.height = 180;
5832
+ ctx2d.fillStyle = "#ffffff";
5833
+ ctx2d.fillRect(0, 0, 140, 180);
5834
+ ctx2d.strokeStyle = "#cbd5e1";
5835
+ ctx2d.lineWidth = 1;
5836
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5837
+ ctx2d.fillStyle = "#334155";
5838
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5839
+ ctx2d.textAlign = "center";
5840
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5841
+ }
5842
+ });
5843
+ }
5844
+ return items;
5845
+ },
5669
5846
  zoomIn: () => {
5670
5847
  isUserZoomed = true;
5671
5848
  scale += 0.15;
@@ -7695,14 +7872,14 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
7695
7872
  };
7696
7873
 
7697
7874
  // 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;
7875
+ 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
7876
  _FilePreviewComponent_decorators = [core.Component({
7700
7877
  selector: "fp-file-preview",
7701
7878
  standalone: true,
7702
7879
  imports: [common.CommonModule],
7703
7880
  template: `<div #container [style.width]="'100%'" [style.height]="'100%'"></div>`,
7704
7881
  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 })];
7882
+ })], _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
7883
  exports.FilePreviewComponent = class FilePreviewComponent {
7707
7884
  constructor(ngZone) {
7708
7885
  this.ngZone = ngZone;
@@ -7714,7 +7891,9 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7714
7891
  __publicField(this, "error", __runInitializers(_init, 28, this, new core.EventEmitter())), __runInitializers(_init, 31, this);
7715
7892
  __publicField(this, "pageChange", __runInitializers(_init, 32, this, new core.EventEmitter())), __runInitializers(_init, 35, this);
7716
7893
  __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);
7894
+ __publicField(this, "rotate", __runInitializers(_init, 40, this, new core.EventEmitter())), __runInitializers(_init, 43, this);
7895
+ __publicField(this, "destroyed", __runInitializers(_init, 44, this, new core.EventEmitter())), __runInitializers(_init, 47, this);
7896
+ __publicField(this, "container", __runInitializers(_init, 48, this)), __runInitializers(_init, 51, this);
7718
7897
  __publicField(this, "viewer", null);
7719
7898
  __publicField(this, "instance", null);
7720
7899
  }
@@ -7739,6 +7918,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7739
7918
  viewer.on("zoom-change", (data) => {
7740
7919
  this.ngZone.run(() => this.zoomChange.emit(data));
7741
7920
  });
7921
+ viewer.on("rotate", (data) => {
7922
+ this.ngZone.run(() => this.rotate.emit(data));
7923
+ });
7924
+ viewer.on("destroy", () => {
7925
+ this.ngZone.run(() => this.destroyed.emit());
7926
+ });
7742
7927
  this.render();
7743
7928
  }
7744
7929
  ngOnChanges(changes) {
@@ -7773,6 +7958,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
7773
7958
  getInstance() {
7774
7959
  return this.instance;
7775
7960
  }
7961
+ getViewer() {
7962
+ return this.viewer;
7963
+ }
7964
+ destroy() {
7965
+ this.viewer?.destroy();
7966
+ }
7776
7967
  };
7777
7968
  _init = __decoratorStart();
7778
7969
  __decorateElement(_init, 5, "src", _src_dec, exports.FilePreviewComponent);
@@ -7783,6 +7974,8 @@ __decorateElement(_init, 5, "loaded", _loaded_dec, exports.FilePreviewComponent)
7783
7974
  __decorateElement(_init, 5, "error", _error_dec, exports.FilePreviewComponent);
7784
7975
  __decorateElement(_init, 5, "pageChange", _pageChange_dec, exports.FilePreviewComponent);
7785
7976
  __decorateElement(_init, 5, "zoomChange", _zoomChange_dec, exports.FilePreviewComponent);
7977
+ __decorateElement(_init, 5, "rotate", _rotate_dec, exports.FilePreviewComponent);
7978
+ __decorateElement(_init, 5, "destroyed", _destroyed_dec, exports.FilePreviewComponent);
7786
7979
  __decorateElement(_init, 5, "container", _container_dec, exports.FilePreviewComponent);
7787
7980
  exports.FilePreviewComponent = __decorateElement(_init, 0, "FilePreviewComponent", _FilePreviewComponent_decorators, exports.FilePreviewComponent);
7788
7981
  __runInitializers(_init, 1, exports.FilePreviewComponent);