Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/EPPlus/Core/AutofitHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace OfficeOpenXml.Core
{
internal class AutofitHelper
{
// Approximate width in pixels (at 96 DPI) of the autofilter dropdown arrow rendered by Excel.
private const double AutoFilterArrowWidthPixels = 15d;
private ExcelRangeBase _range;
ITextMeasurer _genericMeasurer = new GenericFontMetricsTextMeasurer();
MeasurementFont _nonExistingFont = new MeasurementFont() { FontFamily = FontSize.NonExistingFont };
Expand Down Expand Up @@ -126,9 +128,19 @@ internal void AutofitColumn(double MinimumWidth, double MaximumWidth)
{
if (af.Collide(fromRow, col, toRow, col) != eAddressCollition.No)
{
var cell = worksheet.Cells[af.Address];
var cell = worksheet.Cells[af._fromRow, col];
var cellStyleId = styles.CellXfs[cell.StyleID];
currentMaxWidth = GetTextLength(cell, textLengthCache, styles, cellStyleId, normalSize, MaximumWidth, currentMaxWidth);
// Reserve room for the autofilter dropdown arrow. The arrow is a fixed-size UI
// element (~15px at 96 DPI), so it is added as a constant converted to column
// width units (normalSize = width of the normal font's reference char in pixels).
// It is intentionally not affected by AutofitScaleFactor, since the arrow's
// pixel size does not shrink when the user requests tighter text margins.
currentMaxWidth += AutoFilterArrowWidthPixels / normalSize;
if (currentMaxWidth >= MaximumWidth)
{
currentMaxWidth = MaximumWidth;
}
}
}
foreach (var cell in worksheet.Cells[fromRow, col, toRow, col])
Expand Down
52 changes: 52 additions & 0 deletions src/EPPlusTest/Issues/WorksheetIssues.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,58 @@ public void i1314()
package.Dispose();
}
}

[TestMethod]
public void AutofitAutofilterTest()
{
using var package = OpenPackage("AutofitAutofilterTest.xlsx", true);

// Two sheets with identical data: one with an autofilter, one without.
// After autofit, the filtered columns should be wider than the unfiltered
// ones by the reserved width of the dropdown arrow.
var wsFilter = package.Workbook.Worksheets.Add("WithFilter");
var wsNoFilter = package.Workbook.Worksheets.Add("NoFilter");

foreach (var ws in new[] { wsFilter, wsNoFilter })
{
// Headers are the widest text in each column - the data below is deliberately
// shorter so the column width is driven by the header (+ the dropdown arrow
// on the filtered sheet).
ws.Cells["A1"].Value = "Department";
ws.Cells["B1"].Value = "Annual Budget";
ws.Cells["C1"].Value = "Region Name";

ws.Cells["A2"].Value = "Sales";
ws.Cells["B2"].Value = 1200;
ws.Cells["C2"].Value = "North";

ws.Cells["A3"].Value = "IT";
ws.Cells["B3"].Value = 980;
ws.Cells["C3"].Value = "West";

ws.Cells["A4"].Value = "HR";
ws.Cells["B4"].Value = 540;
ws.Cells["C4"].Value = "East";
}

// Only one sheet gets the autofilter.
wsFilter.Cells["A1:C4"].AutoFilter = true;

wsFilter.Cells["A1:C4"].AutoFitColumns();
wsNoFilter.Cells["A1:C4"].AutoFitColumns();

for (int col = 1; col <= 3; col++)
{
var filterWidth = wsFilter.Column(col).Width;
var noFilterWidth = wsNoFilter.Column(col).Width;
System.Diagnostics.Debug.WriteLine($"Column {col}: filter={filterWidth}, noFilter={noFilterWidth}");
Assert.IsTrue(filterWidth > noFilterWidth,
$"Column {col}: filtered width ({filterWidth}) should be greater than unfiltered width ({noFilterWidth}).");
}

SaveAndCleanup(package);
}

[TestMethod]
public void i1317()
{
Expand Down
62 changes: 62 additions & 0 deletions src/EPPlusTest/WorkSheetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2180,6 +2180,68 @@ public void AutoFitColumnTest()
Assert.AreEqual(125d, ws.Columns[1].Width, 5d);
SaveAndCleanup(p);
}

[TestMethod]
public void AutofitAutofilterTest()
{
using var package = OpenTemplatePackage("AutoFitAutofilter.xlsx");
var ws = package.Workbook.Worksheets.Add("Sheet1");

// Headers are the widest text in each column - the data below is deliberately
// shorter so the column width is driven by the header + the autofilter dropdown arrow.
ws.Cells["A1"].Value = "Department";
ws.Cells["B1"].Value = "Annual Budget";
ws.Cells["C1"].Value = "Region Name";

// Data rows - all shorter than the headers above them.
ws.Cells["A2"].Value = "Sales";
ws.Cells["B2"].Value = 1200;
ws.Cells["C2"].Value = "North";

ws.Cells["A3"].Value = "IT";
ws.Cells["B3"].Value = 980;
ws.Cells["C3"].Value = "West";

ws.Cells["A4"].Value = "HR";
ws.Cells["B4"].Value = 540;
ws.Cells["C4"].Value = "East";

// Apply autofilter across the header row + data.
ws.Cells["A1:C4"].AutoFilter = true;

// Autofit the columns.
ws.Cells["A1:C4"].AutoFitColumns();

// Inspect what EPPlus actually produced for each column.
System.Diagnostics.Debug.WriteLine($"Column A (Department): {ws.Column(1).Width}");
System.Diagnostics.Debug.WriteLine($"Column B (Annual Budget): {ws.Column(2).Width}");
System.Diagnostics.Debug.WriteLine($"Column C (Region Name): {ws.Column(3).Width}");

// Save the workbook
SaveAndCleanup(package);
}

[TestMethod]
public void AutoFitColumnsWithAutoFilter()
{
var ws = _pck.Workbook.Worksheets.Add("AutofitAutoFilter");
ws.Cells["A1"].Value = "hour";
ws.Cells["B1"].Value = "minute";
ws.Cells["A2"].Value = 12;
ws.Cells["B2"].Value = 30;

ws.Cells["A1:B2"].AutoFilter = true;

ws.Cells["A1:B2"].AutoFitColumns();

// Without the fix, the AutoFilter header row range (A1:B1) is measured as a whole.
// Under the hood, worksheet.Cells["A1:B1"].TextForWidth evaluated to "System.Object[,]" (16 chars),
// which forced a minimum width of ~16.07 points.
// With the fix, the specific cell for each column in the AutoFilter is measured,
// resulting in a narrow width matching "hour" / "minute".
Assert.IsTrue(ws.Column(1).Width < 12d, $"Column 1 width should be small but was {ws.Column(1).Width}");
Assert.IsTrue(ws.Column(2).Width < 12d, $"Column 2 width should be small but was {ws.Column(2).Width}");
}
[TestMethod]
public void CopyOverwrite()
{
Expand Down
Loading