@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.js CHANGED
@@ -3523,26 +3523,6 @@ var ExcelPlugin = class {
3523
3523
  instance.zoomIn?.();
3524
3524
  }
3525
3525
  },
3526
- {
3527
- id: "fit-page",
3528
- icon: "fit-page",
3529
- label: "Fit to View",
3530
- type: "button",
3531
- group: "zoom",
3532
- execute: () => {
3533
- instance.fitToPage?.();
3534
- }
3535
- },
3536
- {
3537
- id: "rotate-cw",
3538
- icon: "rotate-cw",
3539
- label: "Rotate",
3540
- type: "button",
3541
- group: "view",
3542
- execute: () => {
3543
- instance.rotateCW?.();
3544
- }
3545
- },
3546
3526
  {
3547
3527
  id: "download",
3548
3528
  icon: "download",
@@ -3603,12 +3583,11 @@ var ExcelPlugin = class {
3603
3583
  container.appendChild(tabsArea);
3604
3584
  ctx.container.appendChild(container);
3605
3585
  let scale = 1;
3606
- let rotation = 0;
3607
3586
  let currentSheetIndex = 1;
3608
3587
  let sheetNames = [];
3609
3588
  let wb = null;
3610
3589
  const applyTransform = () => {
3611
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3590
+ contentArea.style.transform = `scale(${scale})`;
3612
3591
  contentArea.style.transformOrigin = "top left";
3613
3592
  };
3614
3593
  const calculateFitScale = () => {
@@ -3616,7 +3595,7 @@ var ExcelPlugin = class {
3616
3595
  if (!table) return 1;
3617
3596
  const availW = Math.max(280, container.clientWidth - 48);
3618
3597
  const tW = table.offsetWidth || 800;
3619
- return Math.max(0.65, Math.min(1, availW / tW));
3598
+ return Math.max(0.4, Math.min(1, availW / tW));
3620
3599
  };
3621
3600
  try {
3622
3601
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3671,7 +3650,16 @@ var ExcelPlugin = class {
3671
3650
  }
3672
3651
  });
3673
3652
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3653
+ requestAnimationFrame(() => {
3654
+ scale = calculateFitScale();
3655
+ applyTransform();
3656
+ });
3674
3657
  };
3658
+ const ro = new ResizeObserver(() => {
3659
+ scale = calculateFitScale();
3660
+ applyTransform();
3661
+ });
3662
+ ro.observe(container);
3675
3663
  if (sheetNames.length > 0) {
3676
3664
  sheetNames.forEach((name, idx) => {
3677
3665
  const btn = document.createElement("button");
@@ -3698,6 +3686,7 @@ var ExcelPlugin = class {
3698
3686
  `;
3699
3687
  }
3700
3688
  const cleanup = () => {
3689
+ ro.disconnect();
3701
3690
  container.remove();
3702
3691
  ctx.container.innerHTML = "";
3703
3692
  };
@@ -3719,16 +3708,11 @@ var ExcelPlugin = class {
3719
3708
  },
3720
3709
  fitToPage: () => {
3721
3710
  scale = calculateFitScale();
3722
- rotation = 0;
3723
3711
  applyTransform();
3724
3712
  },
3725
3713
  rotateCW: () => {
3726
- rotation = (rotation + 90) % 360;
3727
- applyTransform();
3728
3714
  },
3729
3715
  rotateCCW: () => {
3730
- rotation = (rotation - 90 + 360) % 360;
3731
- applyTransform();
3732
3716
  },
3733
3717
  goToPage: (page) => {
3734
3718
  if (page > 0 && page <= sheetNames.length) {
@@ -3826,38 +3810,15 @@ var CsvPlugin = class {
3826
3810
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3827
3811
  }
3828
3812
  getToolbarActions(instance) {
3829
- const totalPages = instance.getPageCount?.() ?? 1;
3830
3813
  const actions = [];
3831
3814
  actions.push({
3832
3815
  id: "thumbnails",
3833
3816
  icon: "thumbnails",
3834
- label: "Page Thumbnails",
3817
+ label: "Sheet Thumbnails",
3835
3818
  type: "button",
3836
3819
  group: "navigation",
3837
3820
  execute: () => instance.toggleThumbnails?.()
3838
3821
  });
3839
- if (totalPages > 1) {
3840
- actions.push({
3841
- id: "page-nav",
3842
- icon: "",
3843
- label: "Page Navigation",
3844
- type: "page-nav",
3845
- group: "navigation",
3846
- value: instance.getCurrentPage?.() ?? 1,
3847
- max: totalPages,
3848
- execute: (action, page) => {
3849
- const cur = instance.getCurrentPage?.() ?? 1;
3850
- const max = instance.getPageCount?.() ?? 1;
3851
- if (action === "prev") {
3852
- if (cur > 1) instance.goToPage?.(cur - 1);
3853
- } else if (action === "next") {
3854
- if (cur < max) instance.goToPage?.(cur + 1);
3855
- } else if (typeof page === "number") {
3856
- instance.goToPage?.(page);
3857
- }
3858
- }
3859
- });
3860
- }
3861
3822
  actions.push(
3862
3823
  {
3863
3824
  id: "zoom-out",
@@ -3879,16 +3840,6 @@ var CsvPlugin = class {
3879
3840
  instance.zoomIn?.();
3880
3841
  }
3881
3842
  },
3882
- {
3883
- id: "fit-page",
3884
- icon: "fit-page",
3885
- label: "Fit to View",
3886
- type: "button",
3887
- group: "zoom",
3888
- execute: () => {
3889
- instance.fitToPage?.();
3890
- }
3891
- },
3892
3843
  {
3893
3844
  id: "download",
3894
3845
  icon: "download",
@@ -3931,131 +3882,135 @@ var CsvPlugin = class {
3931
3882
  container.style.overflow = "auto";
3932
3883
  container.style.padding = "16px";
3933
3884
  container.style.boxSizing = "border-box";
3934
- container.style.transformOrigin = "top left";
3885
+ const tableWrapper = document.createElement("div");
3886
+ tableWrapper.style.transformOrigin = "top left";
3935
3887
  const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3936
3888
  const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3937
3889
  const headerLine = allLines[0] || "";
3938
3890
  const headerCells = headerLine.split(delimiter);
3939
3891
  const dataLines = allLines.slice(1);
3940
- const ROWS_PER_PAGE = 100;
3941
- const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3942
- let currentPage = 1;
3943
3892
  const table = document.createElement("table");
3944
3893
  table.style.borderCollapse = "collapse";
3945
- table.style.width = "100%";
3894
+ table.style.minWidth = "100%";
3946
3895
  table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3947
3896
  table.style.fontSize = "13px";
3948
- const renderPage = (pageNum) => {
3949
- currentPage = Math.max(1, Math.min(totalPages, pageNum));
3950
- table.innerHTML = "";
3951
- const headerTr = document.createElement("tr");
3952
- headerCells.forEach((cell) => {
3953
- const th = document.createElement("th");
3954
- th.textContent = cell.trim();
3955
- th.style.border = "1px solid #d0d7de";
3956
- th.style.padding = "8px 12px";
3957
- th.style.backgroundColor = "#f6f8fa";
3958
- th.style.fontWeight = "600";
3959
- th.style.whiteSpace = "nowrap";
3960
- headerTr.appendChild(th);
3961
- });
3962
- table.appendChild(headerTr);
3963
- const start = (currentPage - 1) * ROWS_PER_PAGE;
3964
- const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
3965
- const pageRows = dataLines.slice(start, end);
3966
- pageRows.forEach((row, idx) => {
3967
- const tr = document.createElement("tr");
3968
- if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3969
- const cells = row.split(delimiter);
3970
- cells.forEach((cell) => {
3971
- const td = document.createElement("td");
3972
- td.textContent = cell.trim();
3973
- td.style.border = "1px solid #d0d7de";
3974
- td.style.padding = "6px 12px";
3975
- td.style.whiteSpace = "nowrap";
3976
- tr.appendChild(td);
3977
- });
3978
- table.appendChild(tr);
3897
+ table.style.background = "#ffffff";
3898
+ table.style.border = "1px solid #d0d7de";
3899
+ const headerTr = document.createElement("tr");
3900
+ headerCells.forEach((cell) => {
3901
+ const th = document.createElement("th");
3902
+ th.textContent = cell.trim();
3903
+ th.style.border = "1px solid #d0d7de";
3904
+ th.style.padding = "8px 12px";
3905
+ th.style.backgroundColor = "#f6f8fa";
3906
+ th.style.fontWeight = "600";
3907
+ th.style.whiteSpace = "nowrap";
3908
+ headerTr.appendChild(th);
3909
+ });
3910
+ table.appendChild(headerTr);
3911
+ dataLines.forEach((row, idx) => {
3912
+ const tr = document.createElement("tr");
3913
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3914
+ const cells = row.split(delimiter);
3915
+ cells.forEach((cell) => {
3916
+ const td = document.createElement("td");
3917
+ td.textContent = cell.trim();
3918
+ td.style.border = "1px solid #d0d7de";
3919
+ td.style.padding = "6px 12px";
3920
+ td.style.whiteSpace = "nowrap";
3921
+ tr.appendChild(td);
3979
3922
  });
3980
- container.scrollTop = 0;
3981
- ctx.emit("page-change", { page: currentPage, total: totalPages });
3982
- };
3983
- renderPage(1);
3984
- container.appendChild(table);
3923
+ table.appendChild(tr);
3924
+ });
3925
+ tableWrapper.appendChild(table);
3926
+ container.appendChild(tableWrapper);
3985
3927
  ctx.container.appendChild(container);
3986
3928
  let scale = 1;
3929
+ const calculateFitScale = () => {
3930
+ const availW = Math.max(280, container.clientWidth - 48);
3931
+ const tW = table.offsetWidth || 800;
3932
+ return Math.max(0.4, Math.min(1, availW / tW));
3933
+ };
3987
3934
  const applyScale = () => {
3988
- container.style.transform = `scale(${scale})`;
3989
- container.style.transformOrigin = "top left";
3935
+ tableWrapper.style.transform = `scale(${scale})`;
3990
3936
  };
3937
+ requestAnimationFrame(() => {
3938
+ scale = calculateFitScale();
3939
+ applyScale();
3940
+ });
3941
+ const ro = new ResizeObserver(() => {
3942
+ scale = calculateFitScale();
3943
+ applyScale();
3944
+ });
3945
+ ro.observe(container);
3991
3946
  const cleanup = () => {
3947
+ ro.disconnect();
3992
3948
  container.remove();
3993
3949
  ctx.container.innerHTML = "";
3994
3950
  };
3995
3951
  ctx.signal.addEventListener("abort", cleanup);
3996
3952
  return {
3997
3953
  destroy: cleanup,
3998
- getPageCount: () => totalPages,
3999
- getCurrentPage: () => currentPage,
4000
- goToPage: (page) => renderPage(page),
3954
+ getPageCount: () => 1,
3955
+ getCurrentPage: () => 1,
3956
+ goToPage: () => {
3957
+ },
4001
3958
  getThumbnails: () => {
4002
- const thumbnails = [];
4003
- for (let i = 0; i < totalPages; i++) {
4004
- const pageIdx = i;
4005
- const startRow = pageIdx * ROWS_PER_PAGE + 1;
4006
- const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4007
- const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4008
- thumbnails.push({
4009
- index: pageIdx,
4010
- label,
4011
- render: async (canvas) => {
4012
- canvas.width = 140;
4013
- canvas.height = 100;
4014
- const c = canvas.getContext("2d");
4015
- if (!c) return;
4016
- c.fillStyle = "#ffffff";
4017
- c.fillRect(0, 0, canvas.width, canvas.height);
4018
- c.fillStyle = "#2563eb";
4019
- c.fillRect(0, 0, canvas.width, 16);
4020
- c.fillStyle = "#ffffff";
4021
- c.font = "bold 8px sans-serif";
4022
- c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4023
- const startY = 17;
4024
- const rowH = 13;
4025
- const colW = 28;
4026
- const cols = Math.min(4, headerCells.length || 4);
4027
- c.fillStyle = "#f1f5f9";
4028
- c.fillRect(0, startY, canvas.width, rowH);
4029
- c.strokeStyle = "#cbd5e1";
4030
- c.lineWidth = 0.8;
4031
- c.strokeRect(0, startY, canvas.width, rowH);
4032
- c.fillStyle = "#64748b";
4033
- c.font = "bold 7px sans-serif";
4034
- for (let ci = 0; ci < cols; ci++) {
4035
- const hName = headerCells[ci] || `Col ${ci + 1}`;
4036
- c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4037
- }
4038
- const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4039
- for (let ri = 0; ri < rowsToPreview.length; ri++) {
4040
- const y = startY + (ri + 1) * rowH;
4041
- if (y > canvas.height - 2) break;
4042
- c.strokeStyle = "#e2e8f0";
4043
- c.strokeRect(0, y, canvas.width, rowH);
4044
- const cVals = rowsToPreview[ri].split(delimiter);
4045
- c.fillStyle = "#334155";
4046
- c.font = "6px sans-serif";
4047
- for (let ci = 0; ci < cols; ci++) {
4048
- const val = cVals[ci] || "";
4049
- c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
3959
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3960
+ return [{
3961
+ index: 0,
3962
+ label: sheetName,
3963
+ render: async (canvas) => {
3964
+ canvas.width = 140;
3965
+ canvas.height = 100;
3966
+ const c = canvas.getContext("2d");
3967
+ if (!c) return;
3968
+ c.fillStyle = "#ffffff";
3969
+ c.fillRect(0, 0, canvas.width, canvas.height);
3970
+ c.fillStyle = "#107c41";
3971
+ c.fillRect(0, 0, canvas.width, 16);
3972
+ c.fillStyle = "#ffffff";
3973
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3974
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
3975
+ const startY = 17;
3976
+ const rowH = 13;
3977
+ const colW = 30;
3978
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
3979
+ c.fillStyle = "#f1f5f9";
3980
+ c.fillRect(0, startY, canvas.width, rowH);
3981
+ c.strokeStyle = "#cbd5e1";
3982
+ c.lineWidth = 0.8;
3983
+ c.strokeRect(0, startY, canvas.width, rowH);
3984
+ c.fillStyle = "#64748b";
3985
+ c.font = "bold 7px sans-serif";
3986
+ for (let ci = 0; ci < cols.length; ci++) {
3987
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
3988
+ }
3989
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
3990
+ const y = startY + (ri + 1) * rowH;
3991
+ if (y > canvas.height - 2) break;
3992
+ c.fillStyle = "#f8fafc";
3993
+ c.fillRect(0, y, 12, rowH);
3994
+ c.fillStyle = "#94a3b8";
3995
+ c.font = "6px sans-serif";
3996
+ c.fillText(String(ri + 1), 3, y + 9);
3997
+ c.strokeStyle = "#e2e8f0";
3998
+ c.strokeRect(0, y, canvas.width, rowH);
3999
+ const rowCells = dataLines[ri].split(delimiter);
4000
+ c.fillStyle = "#334155";
4001
+ c.font = "6px sans-serif";
4002
+ for (let ci = 0; ci < cols.length; ci++) {
4003
+ const val = (rowCells[ci] || "").trim();
4004
+ if (val) {
4005
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4050
4006
  }
4051
4007
  }
4052
- c.strokeStyle = "#cbd5e1";
4053
- c.lineWidth = 1;
4054
- c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4055
4008
  }
4056
- });
4057
- }
4058
- return thumbnails;
4009
+ c.strokeStyle = "#cbd5e1";
4010
+ c.lineWidth = 1;
4011
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4012
+ }
4013
+ }];
4059
4014
  },
4060
4015
  zoomIn: () => {
4061
4016
  scale += 0.1;
@@ -4071,11 +4026,13 @@ var CsvPlugin = class {
4071
4026
  applyScale();
4072
4027
  },
4073
4028
  fitToPage: () => {
4074
- const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4075
- const tW = table.offsetWidth || 600;
4076
- scale = Math.max(0.35, Math.min(1, availW / tW));
4029
+ scale = calculateFitScale();
4077
4030
  applyScale();
4078
4031
  },
4032
+ rotateCW: () => {
4033
+ },
4034
+ rotateCCW: () => {
4035
+ },
4079
4036
  download: () => {
4080
4037
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
4081
4038
  const url = URL.createObjectURL(blob);
@@ -4187,26 +4144,6 @@ var CodePlugin = class {
4187
4144
  instance.zoomIn?.();
4188
4145
  }
4189
4146
  },
4190
- {
4191
- id: "fit-page",
4192
- icon: "fit-page",
4193
- label: "Fit to View",
4194
- type: "button",
4195
- group: "zoom",
4196
- execute: () => {
4197
- instance.fitToPage?.();
4198
- }
4199
- },
4200
- {
4201
- id: "rotate-cw",
4202
- icon: "rotate-cw",
4203
- label: "Rotate",
4204
- type: "button",
4205
- group: "view",
4206
- execute: () => {
4207
- instance.rotateCW?.();
4208
- }
4209
- },
4210
4147
  {
4211
4148
  id: "copy",
4212
4149
  icon: "copy",
@@ -5350,6 +5287,166 @@ var ThreeDPlugin = class {
5350
5287
  function threeDPlugin() {
5351
5288
  return new ThreeDPlugin();
5352
5289
  }
5290
+ function cleanRtfText(raw) {
5291
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5292
+ let num = parseInt(code, 10);
5293
+ if (num < 0) num += 65536;
5294
+ return String.fromCharCode(num);
5295
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5296
+ return String.fromCharCode(parseInt(hex, 16));
5297
+ }).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();
5298
+ }
5299
+ function parseRtfTables(rtfText) {
5300
+ const rowSegments = rtfText.split(/\\row\b/);
5301
+ if (rowSegments.length <= 1) return [];
5302
+ const rawTables = [];
5303
+ let currentTable = null;
5304
+ let charOffset = 0;
5305
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5306
+ const segment = rowSegments[i];
5307
+ const segmentStart = charOffset;
5308
+ const segmentEnd = charOffset + segment.length + 4;
5309
+ const cellParts = segment.split(/\\cell\b/);
5310
+ if (cellParts.length > 1) {
5311
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5312
+ let cellWidths = [];
5313
+ if (cellxMatches.length > 0) {
5314
+ let seq = [cellxMatches[0]];
5315
+ for (let k = 1; k < cellxMatches.length; k++) {
5316
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5317
+ else break;
5318
+ }
5319
+ let prev = 0;
5320
+ cellWidths = seq.map((x) => {
5321
+ const w = x - prev;
5322
+ prev = x;
5323
+ return w;
5324
+ });
5325
+ }
5326
+ const cells = [];
5327
+ for (let c = 0; c < cellParts.length - 1; c++) {
5328
+ let rawCell = cellParts[c];
5329
+ if (c === 0) {
5330
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5331
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5332
+ }
5333
+ cells.push(cleanRtfText(rawCell));
5334
+ }
5335
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5336
+ if (isNewTable) {
5337
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5338
+ const cleanPrev = cleanRtfText(prevTextSlice);
5339
+ const anchorBefore = cleanPrev.slice(-80).trim();
5340
+ currentTable = {
5341
+ id: `RTF_TABLE_${rawTables.length}`,
5342
+ startPos: segmentStart,
5343
+ lastEnd: segmentEnd,
5344
+ rows: [],
5345
+ columnWidths: cellWidths,
5346
+ anchorBefore,
5347
+ anchorAfter: ""
5348
+ };
5349
+ rawTables.push(currentTable);
5350
+ }
5351
+ if (currentTable) {
5352
+ currentTable.rows.push({
5353
+ cells,
5354
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5355
+ });
5356
+ currentTable.lastEnd = segmentEnd;
5357
+ }
5358
+ }
5359
+ charOffset += segment.length + 4;
5360
+ }
5361
+ rawTables.forEach((tbl) => {
5362
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5363
+ const cleanAfter = cleanRtfText(afterSlice);
5364
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5365
+ });
5366
+ return rawTables.map((tbl) => {
5367
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5368
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5369
+ return {
5370
+ id: tbl.id,
5371
+ rows: tbl.rows.map((r) => r.cells),
5372
+ colPercents,
5373
+ anchorBefore: tbl.anchorBefore,
5374
+ anchorAfter: tbl.anchorAfter
5375
+ };
5376
+ });
5377
+ }
5378
+ function renderRtfTableElement(table) {
5379
+ const tableEl = document.createElement("table");
5380
+ tableEl.className = "fp-rtf-table";
5381
+ tableEl.style.width = "100%";
5382
+ tableEl.style.borderCollapse = "collapse";
5383
+ tableEl.style.margin = "16px 0 20px";
5384
+ tableEl.style.fontSize = "10pt";
5385
+ tableEl.style.lineHeight = "1.5";
5386
+ tableEl.style.border = "1px solid #cbd5e1";
5387
+ tableEl.style.borderRadius = "4px";
5388
+ tableEl.style.boxSizing = "border-box";
5389
+ tableEl.style.backgroundColor = "#ffffff";
5390
+ const thead = document.createElement("thead");
5391
+ const tbody = document.createElement("tbody");
5392
+ table.rows.forEach((rowCells, rIdx) => {
5393
+ const tr = document.createElement("tr");
5394
+ const isHeader = rIdx === 0;
5395
+ if (isHeader) {
5396
+ tr.style.backgroundColor = "#f8fafc";
5397
+ tr.style.borderBottom = "2px solid #cbd5e1";
5398
+ } else {
5399
+ tr.style.borderBottom = "1px solid #e2e8f0";
5400
+ if (rIdx % 2 === 0) {
5401
+ tr.style.backgroundColor = "#fcfdfe";
5402
+ }
5403
+ }
5404
+ rowCells.forEach((cellText, cIdx) => {
5405
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5406
+ cellEl.style.padding = "8px 12px";
5407
+ cellEl.style.border = "1px solid #cbd5e1";
5408
+ cellEl.style.verticalAlign = "top";
5409
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5410
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5411
+ if (table.colPercents && table.colPercents[cIdx]) {
5412
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5413
+ }
5414
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5415
+ cellEl.style.textAlign = "center";
5416
+ } else {
5417
+ cellEl.style.textAlign = "left";
5418
+ }
5419
+ cellEl.textContent = cellText;
5420
+ tr.appendChild(cellEl);
5421
+ });
5422
+ if (isHeader) {
5423
+ thead.appendChild(tr);
5424
+ } else {
5425
+ tbody.appendChild(tr);
5426
+ }
5427
+ });
5428
+ tableEl.appendChild(thead);
5429
+ tableEl.appendChild(tbody);
5430
+ return tableEl;
5431
+ }
5432
+ function extractMediaFingerprint(el) {
5433
+ const img = el.tagName.toLowerCase() === "img" ? el : el.querySelector("img");
5434
+ if (img && img.src) {
5435
+ const s = img.src;
5436
+ return s.length > 300 ? `img_${s.slice(0, 80)}_${s.length}_${s.slice(-80)}` : `img_${s}`;
5437
+ }
5438
+ const innerImg = el.querySelector("image");
5439
+ if (innerImg) {
5440
+ const h = innerImg.getAttribute("xlink:href") || innerImg.getAttribute("href") || "";
5441
+ if (h) return h.length > 300 ? `svgimg_${h.slice(0, 80)}_${h.length}_${h.slice(-80)}` : `svgimg_${h}`;
5442
+ }
5443
+ const svg = el.tagName.toLowerCase() === "svg" ? el : el.querySelector("svg");
5444
+ if (svg) {
5445
+ const vb = svg.getAttribute("viewBox") || "";
5446
+ return `svg_${vb}_${svg.innerHTML.length}`;
5447
+ }
5448
+ return null;
5449
+ }
5353
5450
  var RtfPlugin = class {
5354
5451
  id = "rtf";
5355
5452
  name = "Rich Text Format (RTF)";
@@ -5385,6 +5482,14 @@ var RtfPlugin = class {
5385
5482
  }
5386
5483
  });
5387
5484
  actions.push(
5485
+ {
5486
+ id: "thumbnails",
5487
+ icon: "thumbnails",
5488
+ label: "Thumbnails",
5489
+ type: "button",
5490
+ group: "view",
5491
+ execute: () => instance.toggleThumbnails?.()
5492
+ },
5388
5493
  {
5389
5494
  id: "zoom-out",
5390
5495
  icon: "zoom-out",
@@ -5512,9 +5617,29 @@ var RtfPlugin = class {
5512
5617
  if (typeof RTFJS.loggingEnabled === "function") {
5513
5618
  RTFJS.loggingEnabled(false);
5514
5619
  }
5515
- const doc = new RTFJS.Document(ctx.buffer, {});
5620
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5621
+ const tables = parseRtfTables(rawText);
5622
+ const seenMediaSignatures = /* @__PURE__ */ new Set();
5623
+ const doc = new RTFJS.Document(ctx.buffer, {
5624
+ onPicture: (isLegacy, createPic) => {
5625
+ if (isLegacy === true) {
5626
+ return null;
5627
+ }
5628
+ const pic = createPic();
5629
+ if (!pic) return null;
5630
+ const fp = extractMediaFingerprint(pic);
5631
+ if (fp) {
5632
+ if (seenMediaSignatures.has(fp)) {
5633
+ return null;
5634
+ }
5635
+ seenMediaSignatures.add(fp);
5636
+ }
5637
+ return pic;
5638
+ }
5639
+ });
5516
5640
  const htmlElements = await doc.render();
5517
5641
  const contentNodes = [];
5642
+ const seenInContentBlocks = /* @__PURE__ */ new Set();
5518
5643
  const extractBlocks = (nodes) => {
5519
5644
  for (const item of nodes) {
5520
5645
  if (!item || !(item instanceof HTMLElement)) continue;
@@ -5526,6 +5651,32 @@ var RtfPlugin = class {
5526
5651
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5527
5652
  extractBlocks(Array.from(item.children));
5528
5653
  } else {
5654
+ const fp = extractMediaFingerprint(item);
5655
+ if (fp) {
5656
+ if (seenInContentBlocks.has(fp)) {
5657
+ continue;
5658
+ }
5659
+ seenInContentBlocks.add(fp);
5660
+ }
5661
+ const hasMedia = item.querySelector("img, svg, canvas, table") !== null || ["img", "svg", "table"].includes(tag);
5662
+ const textContent = (item.textContent || "").trim();
5663
+ if (!hasMedia && textContent.length === 0) {
5664
+ continue;
5665
+ }
5666
+ const svgs = item.querySelectorAll("svg, img");
5667
+ svgs.forEach((s) => {
5668
+ const el = s;
5669
+ el.style.maxWidth = "100%";
5670
+ el.style.maxHeight = "360px";
5671
+ el.style.display = "block";
5672
+ el.style.margin = "12px auto";
5673
+ if (s.tagName.toLowerCase() === "svg") {
5674
+ s.removeAttribute("width");
5675
+ s.removeAttribute("height");
5676
+ el.style.width = "100%";
5677
+ el.style.maxHeight = "360px";
5678
+ }
5679
+ });
5529
5680
  if (tag === "p" || tag === "div") {
5530
5681
  item.style.display = "block";
5531
5682
  item.style.marginBottom = "12px";
@@ -5536,52 +5687,120 @@ var RtfPlugin = class {
5536
5687
  }
5537
5688
  };
5538
5689
  extractBlocks(htmlElements);
5539
- wrapper.innerHTML = "";
5540
- contentNodes.forEach((node) => wrapper.appendChild(node));
5541
- const childHeights = contentNodes.map((c) => {
5542
- const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5543
- const offH = c.offsetHeight || 0;
5544
- const textLen = c.textContent?.trim().length || 0;
5545
- const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
5546
- return Math.max(rectH, offH, estH);
5690
+ tables.forEach((table) => {
5691
+ const tableEl = renderRtfTableElement(table);
5692
+ let inserted = false;
5693
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5694
+ const keyword = table.anchorBefore.slice(-35).trim();
5695
+ for (let i = 0; i < contentNodes.length; i++) {
5696
+ if (contentNodes[i].textContent?.includes(keyword)) {
5697
+ contentNodes.splice(i + 1, 0, tableEl);
5698
+ inserted = true;
5699
+ break;
5700
+ }
5701
+ }
5702
+ }
5703
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5704
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5705
+ for (let i = 0; i < contentNodes.length; i++) {
5706
+ if (contentNodes[i].textContent?.includes(keyword)) {
5707
+ contentNodes.splice(i, 0, tableEl);
5708
+ inserted = true;
5709
+ break;
5710
+ }
5711
+ }
5712
+ }
5713
+ if (!inserted) {
5714
+ if (contentNodes.length > 2) {
5715
+ contentNodes.splice(2, 0, tableEl);
5716
+ } else {
5717
+ contentNodes.push(tableEl);
5718
+ }
5719
+ }
5547
5720
  });
5548
- wrapper.innerHTML = "";
5549
- const createRtfCard = () => {
5721
+ const createRtfCard = (pageNum) => {
5550
5722
  const card = document.createElement("div");
5551
5723
  card.className = "fp-rtf-page-card";
5724
+ card.setAttribute("data-page-number", String(pageNum));
5552
5725
  card.style.backgroundColor = "#ffffff";
5553
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5726
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08), 0 1px 3px rgba(0,0,0,0.04)";
5554
5727
  card.style.borderRadius = "4px";
5555
- card.style.padding = "72px 56px";
5728
+ card.style.padding = "64px 56px";
5556
5729
  card.style.width = "816px";
5557
5730
  card.style.minHeight = "1056px";
5731
+ card.style.maxHeight = "1056px";
5732
+ card.style.overflow = "hidden";
5558
5733
  card.style.boxSizing = "border-box";
5559
5734
  card.style.marginBottom = "24px";
5560
5735
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5561
5736
  card.style.lineHeight = "1.6";
5562
5737
  card.style.color = "#1e293b";
5738
+ card.style.position = "relative";
5563
5739
  return card;
5564
5740
  };
5565
- let curCard = createRtfCard();
5741
+ wrapper.innerHTML = "";
5742
+ const measureCard = createRtfCard(0);
5743
+ measureCard.style.maxHeight = "none";
5744
+ measureCard.style.height = "auto";
5745
+ wrapper.appendChild(measureCard);
5746
+ contentNodes.forEach((node) => measureCard.appendChild(node));
5747
+ const childHeights = contentNodes.map((c) => {
5748
+ const rect = c.getBoundingClientRect ? c.getBoundingClientRect() : null;
5749
+ const rectH = rect ? rect.height : 0;
5750
+ const offH = c.offsetHeight || 0;
5751
+ let realH = Math.max(rectH, offH);
5752
+ try {
5753
+ const cs = window.getComputedStyle(c);
5754
+ realH += (parseFloat(cs.marginTop) || 0) + (parseFloat(cs.marginBottom) || 0);
5755
+ } catch {
5756
+ }
5757
+ const hasMedia = c.querySelector("img, svg") !== null || ["img", "svg"].includes(c.tagName.toLowerCase());
5758
+ const hasTable = c.querySelector("table") !== null || c.tagName.toLowerCase() === "table";
5759
+ if (hasMedia) {
5760
+ realH = Math.max(realH, 300);
5761
+ } else if (hasTable) {
5762
+ realH = Math.max(realH, 240);
5763
+ }
5764
+ if (realH > 0) return realH;
5765
+ const textLen = c.textContent?.trim().length || 0;
5766
+ return Math.max(28, Math.ceil(textLen / 75) * 26 + 16);
5767
+ });
5768
+ wrapper.innerHTML = "";
5769
+ let curCard = createRtfCard(1);
5566
5770
  wrapper.appendChild(curCard);
5567
5771
  pageElements = [curCard];
5568
5772
  let curH = 0;
5569
- const maxH = 912;
5773
+ const maxH = 860;
5570
5774
  for (let i = 0; i < contentNodes.length; i++) {
5571
5775
  const child = contentNodes[i];
5572
5776
  const chH = childHeights[i];
5573
- curCard.appendChild(child);
5574
- curH += chH;
5575
- if (curH >= maxH && i < contentNodes.length - 1) {
5576
- curCard = createRtfCard();
5777
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5778
+ if (curH === 0 && isChildEmpty) {
5779
+ continue;
5780
+ }
5781
+ const isTable = child.querySelector("table") !== null || child.tagName.toLowerCase() === "table";
5782
+ const shouldBreakBeforeTable = isTable && curH > 400 && curCard.childNodes.length > 0;
5783
+ if ((curH + chH > maxH || shouldBreakBeforeTable) && curCard.childNodes.length > 0) {
5784
+ curCard = createRtfCard(pageElements.length + 1);
5577
5785
  wrapper.appendChild(curCard);
5578
5786
  pageElements.push(curCard);
5579
5787
  curH = 0;
5580
5788
  }
5789
+ curCard.appendChild(child);
5790
+ curH += chH;
5791
+ }
5792
+ pageElements = pageElements.filter((card) => {
5793
+ const hasText = (card.textContent || "").trim().length > 0;
5794
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5795
+ return hasText || hasMedia;
5796
+ });
5797
+ if (pageElements.length === 0) {
5798
+ pageElements = [curCard];
5581
5799
  }
5582
5800
  } catch (err) {
5583
5801
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5584
5802
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5803
+ const tables = parseRtfTables(text);
5585
5804
  const rawPages = text.split(/\\page\b/).map((segment) => {
5586
5805
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5587
5806
  }).filter((p) => p.length > 0);
@@ -5592,7 +5811,7 @@ var RtfPlugin = class {
5592
5811
  pageCard.style.backgroundColor = "#ffffff";
5593
5812
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5594
5813
  pageCard.style.borderRadius = "4px";
5595
- pageCard.style.padding = "72px 56px";
5814
+ pageCard.style.padding = "64px 56px";
5596
5815
  pageCard.style.width = "816px";
5597
5816
  pageCard.style.minHeight = "1056px";
5598
5817
  pageCard.style.boxSizing = "border-box";
@@ -5603,6 +5822,9 @@ var RtfPlugin = class {
5603
5822
  pageCard.style.whiteSpace = "pre-wrap";
5604
5823
  pageCard.style.display = i === 0 ? "block" : "none";
5605
5824
  pageCard.textContent = pages[i];
5825
+ if (i === 1 && tables.length > 0) {
5826
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5827
+ }
5606
5828
  wrapper.appendChild(pageCard);
5607
5829
  pageElements.push(pageCard);
5608
5830
  }
@@ -5633,6 +5855,32 @@ var RtfPlugin = class {
5633
5855
  getPageCount: () => totalPages,
5634
5856
  getCurrentPage: () => currentPage,
5635
5857
  goToPage: (page) => showPage(page),
5858
+ getThumbnails: async () => {
5859
+ const items = [];
5860
+ for (let idx = 0; idx < totalPages; idx++) {
5861
+ const pageIdx = idx;
5862
+ items.push({
5863
+ index: pageIdx,
5864
+ label: `Page ${pageIdx + 1}`,
5865
+ render: async (canvas) => {
5866
+ const ctx2d = canvas.getContext("2d");
5867
+ if (!ctx2d) return;
5868
+ canvas.width = 140;
5869
+ canvas.height = 180;
5870
+ ctx2d.fillStyle = "#ffffff";
5871
+ ctx2d.fillRect(0, 0, 140, 180);
5872
+ ctx2d.strokeStyle = "#cbd5e1";
5873
+ ctx2d.lineWidth = 1;
5874
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5875
+ ctx2d.fillStyle = "#334155";
5876
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5877
+ ctx2d.textAlign = "center";
5878
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5879
+ }
5880
+ });
5881
+ }
5882
+ return items;
5883
+ },
5636
5884
  zoomIn: () => {
5637
5885
  isUserZoomed = true;
5638
5886
  scale += 0.15;
@@ -7662,14 +7910,14 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
7662
7910
  };
7663
7911
 
7664
7912
  // src/angular.ts
7665
- var _container_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
7913
+ 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;
7666
7914
  _FilePreviewComponent_decorators = [Component({
7667
7915
  selector: "fp-file-preview",
7668
7916
  standalone: true,
7669
7917
  imports: [CommonModule],
7670
7918
  template: `<div #container [style.width]="'100%'" [style.height]="'100%'"></div>`,
7671
7919
  changeDetection: ChangeDetectionStrategy.OnPush
7672
- })], _src_dec = [Input({ required: true })], _plugins_dec = [Input()], _options_dec = [Input()], _loading_dec = [Output()], _loaded_dec = [Output()], _error_dec = [Output()], _pageChange_dec = [Output()], _zoomChange_dec = [Output()], _container_dec = [ViewChild("container", { static: true })];
7920
+ })], _src_dec = [Input({ required: true })], _plugins_dec = [Input()], _options_dec = [Input()], _loading_dec = [Output()], _loaded_dec = [Output()], _error_dec = [Output()], _pageChange_dec = [Output()], _zoomChange_dec = [Output()], _rotate_dec = [Output()], _destroyed_dec = [Output()], _container_dec = [ViewChild("container", { static: true })];
7673
7921
  var FilePreviewComponent = class {
7674
7922
  constructor(ngZone) {
7675
7923
  this.ngZone = ngZone;
@@ -7681,7 +7929,9 @@ var FilePreviewComponent = class {
7681
7929
  __publicField(this, "error", __runInitializers(_init, 28, this, new EventEmitter$1())), __runInitializers(_init, 31, this);
7682
7930
  __publicField(this, "pageChange", __runInitializers(_init, 32, this, new EventEmitter$1())), __runInitializers(_init, 35, this);
7683
7931
  __publicField(this, "zoomChange", __runInitializers(_init, 36, this, new EventEmitter$1())), __runInitializers(_init, 39, this);
7684
- __publicField(this, "container", __runInitializers(_init, 40, this)), __runInitializers(_init, 43, this);
7932
+ __publicField(this, "rotate", __runInitializers(_init, 40, this, new EventEmitter$1())), __runInitializers(_init, 43, this);
7933
+ __publicField(this, "destroyed", __runInitializers(_init, 44, this, new EventEmitter$1())), __runInitializers(_init, 47, this);
7934
+ __publicField(this, "container", __runInitializers(_init, 48, this)), __runInitializers(_init, 51, this);
7685
7935
  __publicField(this, "viewer", null);
7686
7936
  __publicField(this, "instance", null);
7687
7937
  }
@@ -7706,6 +7956,12 @@ var FilePreviewComponent = class {
7706
7956
  viewer.on("zoom-change", (data) => {
7707
7957
  this.ngZone.run(() => this.zoomChange.emit(data));
7708
7958
  });
7959
+ viewer.on("rotate", (data) => {
7960
+ this.ngZone.run(() => this.rotate.emit(data));
7961
+ });
7962
+ viewer.on("destroy", () => {
7963
+ this.ngZone.run(() => this.destroyed.emit());
7964
+ });
7709
7965
  this.render();
7710
7966
  }
7711
7967
  ngOnChanges(changes) {
@@ -7740,6 +7996,12 @@ var FilePreviewComponent = class {
7740
7996
  getInstance() {
7741
7997
  return this.instance;
7742
7998
  }
7999
+ getViewer() {
8000
+ return this.viewer;
8001
+ }
8002
+ destroy() {
8003
+ this.viewer?.destroy();
8004
+ }
7743
8005
  };
7744
8006
  _init = __decoratorStart();
7745
8007
  __decorateElement(_init, 5, "src", _src_dec, FilePreviewComponent);
@@ -7750,6 +8012,8 @@ __decorateElement(_init, 5, "loaded", _loaded_dec, FilePreviewComponent);
7750
8012
  __decorateElement(_init, 5, "error", _error_dec, FilePreviewComponent);
7751
8013
  __decorateElement(_init, 5, "pageChange", _pageChange_dec, FilePreviewComponent);
7752
8014
  __decorateElement(_init, 5, "zoomChange", _zoomChange_dec, FilePreviewComponent);
8015
+ __decorateElement(_init, 5, "rotate", _rotate_dec, FilePreviewComponent);
8016
+ __decorateElement(_init, 5, "destroyed", _destroyed_dec, FilePreviewComponent);
7753
8017
  __decorateElement(_init, 5, "container", _container_dec, FilePreviewComponent);
7754
8018
  FilePreviewComponent = __decorateElement(_init, 0, "FilePreviewComponent", _FilePreviewComponent_decorators, FilePreviewComponent);
7755
8019
  __runInitializers(_init, 1, FilePreviewComponent);