Skip to content
Merged
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
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,18 @@ public class Dto
}
```

#### 4. Multiple column names mapping to the same property.
#### 4. Set Column Visibility(MiniExcelHiddenAttribute)
```csharp
public class Dto
{
public string Name { get; set; }

[ExcelHidden]
public int SecretPoints { get; set; }
}
```

#### 5. Multiple column names mapping to the same property.

```csharp
public class Dto
Expand All @@ -1084,9 +1095,7 @@ public class Dto
}
```



#### 5. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute
#### 6. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute

Since 1.24.0, system supports System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute

Expand All @@ -1102,9 +1111,7 @@ public class TestIssueI4TXGTDto
}
```



#### 6. ExcelColumnAttribute
#### 7. ExcelColumnAttribute

Since V1.26.0, multiple attributes can be simplified like :
```csharp
Expand All @@ -1114,12 +1121,12 @@ Since V1.26.0, multiple attributes can be simplified like :
public string MyProperty { get; set; }
[ExcelColumn(Name = "CreateDate", Index = 1,Format ="yyyy-MM",Width =100)]
public DateTime MyProperty2 { get; set; }
[ExcelColumn(Name = "SecretColumn", Hidden = true)]
public int MyProperty3 { get; set; }
}
```



#### 7. DynamicColumnAttribute
#### 8. DynamicColumnAttribute

Since V1.26.0, we can set the attributes of Column dynamically
```csharp
Expand All @@ -1138,7 +1145,7 @@ Since V1.26.0, we can set the attributes of Column dynamically
```
![image](https://user-images.githubusercontent.com/12729184/164510353-5aecbc4e-c3ce-41e8-b6cf-afd55eb23b68.png)

#### 8. DynamicSheetAttribute
#### 9. DynamicSheetAttribute

Since V1.31.4 we can set the attributes of Sheet dynamically. We can set sheet name and state (visibility).
```csharp
Expand Down
3 changes: 2 additions & 1 deletion src/MiniExcel/Attributes/ExcelColumnAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ public class ExcelColumnAttribute : Attribute

public string Name { get; set; }
public string[] Aliases { get; set; }
public double Width { get; set; } = 9.28515625;
public double Width { get; set; } = 8.42857143;
public string Format { get; set; }
public bool Hidden { get; set; }
public bool Ignore { get; set; }
public ColumnType Type { get; set; } = ColumnType.Value;

Expand Down
11 changes: 11 additions & 0 deletions src/MiniExcel/Attributes/ExcelHiddenAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace MiniExcelLibs.Attributes
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ExcelHiddenAttribute : Attribute
{
public bool ExcelHidden { get; set; }
public ExcelHiddenAttribute(bool excelHidden = true) => ExcelHidden = excelHidden;
}
}
2 changes: 1 addition & 1 deletion src/MiniExcel/MiniExcelLibs.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net45;netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>
<Version>1.42.0</Version>
<Version>1.43.0</Version>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<LangVersion>8</LangVersion>
Expand Down
10 changes: 5 additions & 5 deletions src/MiniExcel/OpenXml/Constants/WorksheetXml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ internal static class WorksheetXml
internal const string StartSheetViews = "<x:sheetViews>";
internal const string EndSheetViews = "</x:sheetViews>";

internal static string StartSheetView( int tabSelected=0, int workbookViewId=0 )
=> $"<x:sheetView tabSelected=\"{tabSelected}\" workbookViewId=\"{workbookViewId}\">";
internal static string StartSheetView(int tabSelected = 0, int workbookViewId = 0, bool rightToLeft = false)
=> $"<x:sheetView tabSelected=\"{tabSelected}\" workbookViewId=\"{workbookViewId}\" rightToLeft=\"{(rightToLeft ? 1 : 0)}\">";
internal const string EndSheetView = "</x:sheetView>";

internal const string StartSheetData = "<x:sheetData>";
Expand Down Expand Up @@ -45,10 +45,10 @@ internal static string PaneSelection( string pane, string activeCell, string sqr
internal const string EndRow = "</x:row>";
internal const string StartCols = "<x:cols>";

internal static string Column(int colIndex, double columnWidth)
=> $@"<x:col min=""{colIndex}"" max=""{colIndex}"" width=""{columnWidth.ToString(CultureInfo.InvariantCulture)}"" customWidth=""1"" />";
internal static string Column(int colIndex, double columnWidth, bool hidden = false)
=> $@"<x:col min=""{colIndex}"" max=""{colIndex}"" width=""{columnWidth.ToString(CultureInfo.InvariantCulture)}"" hidden=""{(hidden ? 1 : 0)}"" customWidth=""1"" />";

private static readonly int _maxColumnLength = Column(int.MaxValue, double.MaxValue).Length;
private static readonly int _maxColumnLength = Column(int.MaxValue, double.MaxValue, true).Length;

public static int GetColumnPlaceholderLength(int columnCount)
=> StartCols.Length + (_maxColumnLength * columnCount) + EndCols.Length;
Expand Down
6 changes: 3 additions & 3 deletions src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,11 @@ private async Task<int> WriteValuesAsync(MiniExcelAsyncStreamWriter writer, obje
if (_configuration.EnableAutoWidth)
{
columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props);
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth);
}
else
{
await WriteColumnsWidthsAsync(writer, ExcelColumnWidth.FromProps(props), cancellationToken);
await WriteColumnsWidthsAsync(writer, ExcelWidthCollection.FromProps(props), cancellationToken);
}

//header
Expand Down Expand Up @@ -351,7 +351,7 @@ private static async Task WriteColumnsWidthsAsync(MiniExcelAsyncStreamWriter wri
await writer.WriteAsync(WorksheetXml.StartCols);
hasWrittenStart = true;
}
await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width));
await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width, column.Hidden));
}

if (!hasWrittenStart)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,14 @@ private ExcellSheetInfo GetSheetInfos(string sheetName)
private string GetSheetViews()
{
// exit early if no style to write
if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0)
if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0 && !_configuration.RightToLeft)
return string.Empty;

var sb = new StringBuilder();

// start sheetViews
sb.Append(WorksheetXml.StartSheetViews);
sb.Append(WorksheetXml.StartSheetView());
sb.Append(WorksheetXml.StartSheetView(rightToLeft: _configuration.RightToLeft));

// Write panes
sb.Append(GetPanes());
Expand Down
6 changes: 3 additions & 3 deletions src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ private int WriteValues(MiniExcelStreamWriter writer, object values)
if (_configuration.EnableAutoWidth)
{
columnWidthsPlaceholderPosition = WriteColumnWidthPlaceholders(writer, maxColumnIndex);
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth);
}
else
{
WriteColumnsWidths(writer, ExcelColumnWidth.FromProps(props));
WriteColumnsWidths(writer, ExcelWidthCollection.FromProps(props));
}

//header
Expand Down Expand Up @@ -299,7 +299,7 @@ private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable
writer.Write(WorksheetXml.StartCols);
hasWrittenStart = true;
}
writer.Write(WorksheetXml.Column(column.Index, column.Width));
writer.Write(WorksheetXml.Column(column.Index, column.Width, column.Hidden));
}

if (hasWrittenStart)
Expand Down
88 changes: 49 additions & 39 deletions src/MiniExcel/OpenXml/ExcelWidthCollection.cs
Original file line number Diff line number Diff line change
@@ -1,71 +1,81 @@
using MiniExcelLibs.Utils;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace MiniExcelLibs.OpenXml
{
public sealed class ExcelColumnWidth
{
// Aptos is the default font for Office 2023 and onwards, over which the width of cells are calculated at the size of 11pt.
// Priorly it was Calibri, which had very similar parameters, so no visual differences should be noticed.
private const double DefaultCellPadding = 5;
private const double Aptos11MaxDigitWidth = 7;
public const double Aptos11Padding = DefaultCellPadding / Aptos11MaxDigitWidth;

public int Index { get; set; }
public double Width { get; set; }
public bool Hidden { get; set; }

public static double GetWidthFromTextLength(double characters)
=> Math.Round(characters + Aptos11Padding, 8);
}

public sealed class ExcelWidthCollection : IReadOnlyCollection<ExcelColumnWidth>
{
private readonly Dictionary<int, ExcelColumnWidth> _columnWidths;
private readonly double _maxWidth;

public IReadOnlyCollection<ExcelColumnWidth> Columns
#if NET45
=> _columnWidths.Values.ToList();
#else
=> _columnWidths.Values;
#endif

internal static IEnumerable<ExcelColumnWidth> FromProps(ICollection<ExcelColumnInfo> props, double? minWidth = null)
private ExcelWidthCollection(ICollection<ExcelColumnWidth> columnWidths, double maxWidth)
{
_maxWidth = ExcelColumnWidth.GetWidthFromTextLength(maxWidth);
_columnWidths = columnWidths.ToDictionary(x => x.Index);
}

internal static ExcelWidthCollection FromProps(ICollection<ExcelColumnInfo> mappings, double? minWidth = null, double maxWidth = 200)
{
var i = 1;
foreach (var p in props)
var columnWidths = new List<ExcelColumnWidth>();

foreach (var map in mappings)
{
if (p?.ExcelColumnWidth != null || minWidth != null)
if ((map?.ExcelHidden ?? false) || map?.ExcelColumnWidth != null || minWidth != null)
{
var colIndex = p?.ExcelColumnIndex + 1;
yield return new ExcelColumnWidth
{
Index = colIndex ?? i,
Width = p?.ExcelColumnWidth ?? minWidth.Value
};
var colIndex = map?.ExcelColumnIndex + 1 ?? i;
var hidden = map?.ExcelHidden ?? false;
var width = map?.ExcelColumnWidth ?? minWidth ?? 8.42857143;

columnWidths.Add(new ExcelColumnWidth { Index = colIndex, Width = width + ExcelColumnWidth.Aptos11Padding, Hidden = hidden});
}

i++;
}
}
}

public sealed class ExcelWidthCollection
{
private readonly Dictionary<int, ExcelColumnWidth> _columnWidths;
private readonly double _maxWidth;

public IEnumerable<ExcelColumnWidth> Columns => _columnWidths.Values;

internal ExcelWidthCollection(double minWidth, double maxWidth, ICollection<ExcelColumnInfo> props)
{
_maxWidth = maxWidth;
_columnWidths = ExcelColumnWidth.FromProps(props, minWidth).ToDictionary(x => x.Index);
return new ExcelWidthCollection(columnWidths, maxWidth);
}

public void AdjustWidth(int columnIndex, string columnValue)
internal void AdjustWidth(int columnIndex, string columnValue)
{
if (!string.IsNullOrEmpty(columnValue)
&& _columnWidths.TryGetValue(columnIndex, out var currentWidth))
if (!string.IsNullOrEmpty(columnValue) && _columnWidths.TryGetValue(columnIndex, out var currentWidth))
{
var adjustedWidth = Math.Max(currentWidth.Width, GetApproximateTextWidth(columnValue.Length));
var desiredWidth = ExcelColumnWidth.GetWidthFromTextLength(columnValue.Length);
var adjustedWidth = Math.Max(currentWidth.Width, desiredWidth);
currentWidth.Width = Math.Min(_maxWidth, adjustedWidth);
}
}

/// <summary>
/// Get the approximate width of the given text for Calibri 11pt
/// </summary>
/// <remarks>
/// Rounds the result to 2 decimal places.
/// </remarks>
public static double GetApproximateTextWidth(int textLength)
{
const double characterWidthFactor = 1.2; // Estimated factor for Calibri, 11pt
const double padding = 2; // Add some padding for extra spacing
public IEnumerator<ExcelColumnWidth> GetEnumerator() => _columnWidths.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

var excelColumnWidth = (textLength * characterWidthFactor) + padding;
return Math.Round(excelColumnWidth, 2);
}
public int Count => _columnWidths.Count;
}
}
3 changes: 2 additions & 1 deletion src/MiniExcel/OpenXml/OpenXmlConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class OpenXmlConfiguration : Configuration
public bool FillMergedCells { get; set; }
public TableStyles TableStyles { get; set; } = TableStyles.Default;
public bool AutoFilter { get; set; } = true;
public bool RightToLeft { get; set; } = false;
public int FreezeRowCount { get; set; } = 1;
public int FreezeColumnCount { get; set; } = 0;
public bool EnableConvertByteArray { get; set; } = true;
Expand All @@ -34,7 +35,7 @@ public class OpenXmlConfiguration : Configuration
/// </summary>
public bool EnableAutoWidth { get; set; }

public double MinWidth { get; set; } = 9.28515625;
public double MinWidth { get; set; } = 8.42857143;

public double MaxWidth { get; set; } = 200;
}
Expand Down
Loading
Loading