Skip to content

Commit ad5914b

Browse files
Brings some fixes and new features from the lastest PRs affecting master (#935)
* Adds right-to-left layout exporting capability * Corrects edge case bug in the parsing of template escape parameters * Refactors cell width writing logic Corrected cell width calculations to default to a cell width of 8.43 characters (64px) and also take into account the 5px padding to add to the xml value which was previously mising and resulted in a discrepancy with the value displayed in the UI. * Adds hidden columns functionality The new `ExcelHiddenAttribute` and property `MiniExcelColumnAttribute.Hidden` now allow for a property to be mapped as a hidden column in Excel's UI. * Bumping project version * Oversights corrections
1 parent d583683 commit ad5914b

13 files changed

Lines changed: 157 additions & 100 deletions

README.md

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,7 +1073,18 @@ public class Dto
10731073
}
10741074
```
10751075

1076-
#### 4. Multiple column names mapping to the same property.
1076+
#### 4. Set Column Visibility(MiniExcelHiddenAttribute)
1077+
```csharp
1078+
public class Dto
1079+
{
1080+
public string Name { get; set; }
1081+
1082+
[ExcelHidden]
1083+
public int SecretPoints { get; set; }
1084+
}
1085+
```
1086+
1087+
#### 5. Multiple column names mapping to the same property.
10771088

10781089
```csharp
10791090
public class Dto
@@ -1084,9 +1095,7 @@ public class Dto
10841095
}
10851096
```
10861097

1087-
1088-
1089-
#### 5. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute
1098+
#### 6. System.ComponentModel.DisplayNameAttribute = ExcelColumnName.excelColumnNameAttribute
10901099

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

@@ -1102,9 +1111,7 @@ public class TestIssueI4TXGTDto
11021111
}
11031112
```
11041113

1105-
1106-
1107-
#### 6. ExcelColumnAttribute
1114+
#### 7. ExcelColumnAttribute
11081115

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

1120-
1121-
1122-
#### 7. DynamicColumnAttribute
1129+
#### 8. DynamicColumnAttribute
11231130

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

1141-
#### 8. DynamicSheetAttribute
1148+
#### 9. DynamicSheetAttribute
11421149

11431150
Since V1.31.4 we can set the attributes of Sheet dynamically. We can set sheet name and state (visibility).
11441151
```csharp

src/MiniExcel/Attributes/ExcelColumnAttribute.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ public class ExcelColumnAttribute : Attribute
1313

1414
public string Name { get; set; }
1515
public string[] Aliases { get; set; }
16-
public double Width { get; set; } = 9.28515625;
16+
public double Width { get; set; } = 8.42857143;
1717
public string Format { get; set; }
18+
public bool Hidden { get; set; }
1819
public bool Ignore { get; set; }
1920
public ColumnType Type { get; set; } = ColumnType.Value;
2021

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
namespace MiniExcelLibs.Attributes
4+
{
5+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
6+
public class ExcelHiddenAttribute : Attribute
7+
{
8+
public bool ExcelHidden { get; set; }
9+
public ExcelHiddenAttribute(bool excelHidden = true) => ExcelHidden = excelHidden;
10+
}
11+
}

src/MiniExcel/MiniExcelLibs.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFrameworks>net45;netstandard2.0;net8.0;net9.0;net10.0</TargetFrameworks>
4-
<Version>1.42.0</Version>
4+
<Version>1.43.0</Version>
55
</PropertyGroup>
66
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
77
<LangVersion>8</LangVersion>

src/MiniExcel/OpenXml/Constants/WorksheetXml.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ internal static class WorksheetXml
1616
internal const string StartSheetViews = "<x:sheetViews>";
1717
internal const string EndSheetViews = "</x:sheetViews>";
1818

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

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

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

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

5353
public static int GetColumnPlaceholderLength(int columnCount)
5454
=> StartCols.Length + (_maxColumnLength * columnCount) + EndCols.Length;

src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,11 @@ private async Task<int> WriteValuesAsync(MiniExcelAsyncStreamWriter writer, obje
229229
if (_configuration.EnableAutoWidth)
230230
{
231231
columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props);
232-
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
232+
widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth);
233233
}
234234
else
235235
{
236-
await WriteColumnsWidthsAsync(writer, ExcelColumnWidth.FromProps(props), cancellationToken);
236+
await WriteColumnsWidthsAsync(writer, ExcelWidthCollection.FromProps(props), cancellationToken);
237237
}
238238

239239
//header
@@ -351,7 +351,7 @@ private static async Task WriteColumnsWidthsAsync(MiniExcelAsyncStreamWriter wri
351351
await writer.WriteAsync(WorksheetXml.StartCols);
352352
hasWrittenStart = true;
353353
}
354-
await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width));
354+
await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width, column.Hidden));
355355
}
356356

357357
if (!hasWrittenStart)

src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ private ExcellSheetInfo GetSheetInfos(string sheetName)
8080
private string GetSheetViews()
8181
{
8282
// exit early if no style to write
83-
if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0)
83+
if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0 && !_configuration.RightToLeft)
8484
return string.Empty;
8585

8686
var sb = new StringBuilder();
8787

8888
// start sheetViews
8989
sb.Append(WorksheetXml.StartSheetViews);
90-
sb.Append(WorksheetXml.StartSheetView());
90+
sb.Append(WorksheetXml.StartSheetView(rightToLeft: _configuration.RightToLeft));
9191

9292
// Write panes
9393
sb.Append(GetPanes());

src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,11 @@ private int WriteValues(MiniExcelStreamWriter writer, object values)
221221
if (_configuration.EnableAutoWidth)
222222
{
223223
columnWidthsPlaceholderPosition = WriteColumnWidthPlaceholders(writer, maxColumnIndex);
224-
widths = new ExcelWidthCollection(_configuration.MinWidth, _configuration.MaxWidth, props);
224+
widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth);
225225
}
226226
else
227227
{
228-
WriteColumnsWidths(writer, ExcelColumnWidth.FromProps(props));
228+
WriteColumnsWidths(writer, ExcelWidthCollection.FromProps(props));
229229
}
230230

231231
//header
@@ -299,7 +299,7 @@ private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable
299299
writer.Write(WorksheetXml.StartCols);
300300
hasWrittenStart = true;
301301
}
302-
writer.Write(WorksheetXml.Column(column.Index, column.Width));
302+
writer.Write(WorksheetXml.Column(column.Index, column.Width, column.Hidden));
303303
}
304304

305305
if (hasWrittenStart)
Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,81 @@
11
using MiniExcelLibs.Utils;
22
using System;
3+
using System.Collections;
34
using System.Collections.Generic;
5+
using System.Diagnostics;
46
using System.Linq;
57

68
namespace MiniExcelLibs.OpenXml
79
{
810
public sealed class ExcelColumnWidth
911
{
12+
// Aptos is the default font for Office 2023 and onwards, over which the width of cells are calculated at the size of 11pt.
13+
// Priorly it was Calibri, which had very similar parameters, so no visual differences should be noticed.
14+
private const double DefaultCellPadding = 5;
15+
private const double Aptos11MaxDigitWidth = 7;
16+
public const double Aptos11Padding = DefaultCellPadding / Aptos11MaxDigitWidth;
17+
1018
public int Index { get; set; }
1119
public double Width { get; set; }
20+
public bool Hidden { get; set; }
21+
22+
public static double GetWidthFromTextLength(double characters)
23+
=> Math.Round(characters + Aptos11Padding, 8);
24+
}
25+
26+
public sealed class ExcelWidthCollection : IReadOnlyCollection<ExcelColumnWidth>
27+
{
28+
private readonly Dictionary<int, ExcelColumnWidth> _columnWidths;
29+
private readonly double _maxWidth;
30+
31+
public IReadOnlyCollection<ExcelColumnWidth> Columns
32+
#if NET45
33+
=> _columnWidths.Values.ToList();
34+
#else
35+
=> _columnWidths.Values;
36+
#endif
1237

13-
internal static IEnumerable<ExcelColumnWidth> FromProps(ICollection<ExcelColumnInfo> props, double? minWidth = null)
38+
private ExcelWidthCollection(ICollection<ExcelColumnWidth> columnWidths, double maxWidth)
39+
{
40+
_maxWidth = ExcelColumnWidth.GetWidthFromTextLength(maxWidth);
41+
_columnWidths = columnWidths.ToDictionary(x => x.Index);
42+
}
43+
44+
internal static ExcelWidthCollection FromProps(ICollection<ExcelColumnInfo> mappings, double? minWidth = null, double maxWidth = 200)
1445
{
1546
var i = 1;
16-
foreach (var p in props)
47+
var columnWidths = new List<ExcelColumnWidth>();
48+
49+
foreach (var map in mappings)
1750
{
18-
if (p?.ExcelColumnWidth != null || minWidth != null)
51+
if ((map?.ExcelHidden ?? false) || map?.ExcelColumnWidth != null || minWidth != null)
1952
{
20-
var colIndex = p?.ExcelColumnIndex + 1;
21-
yield return new ExcelColumnWidth
22-
{
23-
Index = colIndex ?? i,
24-
Width = p?.ExcelColumnWidth ?? minWidth.Value
25-
};
53+
var colIndex = map?.ExcelColumnIndex + 1 ?? i;
54+
var hidden = map?.ExcelHidden ?? false;
55+
var width = map?.ExcelColumnWidth ?? minWidth ?? 8.42857143;
56+
57+
columnWidths.Add(new ExcelColumnWidth { Index = colIndex, Width = width + ExcelColumnWidth.Aptos11Padding, Hidden = hidden});
2658
}
2759

2860
i++;
2961
}
30-
}
31-
}
32-
33-
public sealed class ExcelWidthCollection
34-
{
35-
private readonly Dictionary<int, ExcelColumnWidth> _columnWidths;
36-
private readonly double _maxWidth;
37-
38-
public IEnumerable<ExcelColumnWidth> Columns => _columnWidths.Values;
3962

40-
internal ExcelWidthCollection(double minWidth, double maxWidth, ICollection<ExcelColumnInfo> props)
41-
{
42-
_maxWidth = maxWidth;
43-
_columnWidths = ExcelColumnWidth.FromProps(props, minWidth).ToDictionary(x => x.Index);
63+
return new ExcelWidthCollection(columnWidths, maxWidth);
4464
}
4565

46-
public void AdjustWidth(int columnIndex, string columnValue)
66+
internal void AdjustWidth(int columnIndex, string columnValue)
4767
{
48-
if (!string.IsNullOrEmpty(columnValue)
49-
&& _columnWidths.TryGetValue(columnIndex, out var currentWidth))
68+
if (!string.IsNullOrEmpty(columnValue) && _columnWidths.TryGetValue(columnIndex, out var currentWidth))
5069
{
51-
var adjustedWidth = Math.Max(currentWidth.Width, GetApproximateTextWidth(columnValue.Length));
70+
var desiredWidth = ExcelColumnWidth.GetWidthFromTextLength(columnValue.Length);
71+
var adjustedWidth = Math.Max(currentWidth.Width, desiredWidth);
5272
currentWidth.Width = Math.Min(_maxWidth, adjustedWidth);
5373
}
5474
}
5575

56-
/// <summary>
57-
/// Get the approximate width of the given text for Calibri 11pt
58-
/// </summary>
59-
/// <remarks>
60-
/// Rounds the result to 2 decimal places.
61-
/// </remarks>
62-
public static double GetApproximateTextWidth(int textLength)
63-
{
64-
const double characterWidthFactor = 1.2; // Estimated factor for Calibri, 11pt
65-
const double padding = 2; // Add some padding for extra spacing
76+
public IEnumerator<ExcelColumnWidth> GetEnumerator() => _columnWidths.Values.GetEnumerator();
77+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
6678

67-
var excelColumnWidth = (textLength * characterWidthFactor) + padding;
68-
return Math.Round(excelColumnWidth, 2);
69-
}
79+
public int Count => _columnWidths.Count;
7080
}
7181
}

src/MiniExcel/OpenXml/OpenXmlConfiguration.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class OpenXmlConfiguration : Configuration
99
public bool FillMergedCells { get; set; }
1010
public TableStyles TableStyles { get; set; } = TableStyles.Default;
1111
public bool AutoFilter { get; set; } = true;
12+
public bool RightToLeft { get; set; } = false;
1213
public int FreezeRowCount { get; set; } = 1;
1314
public int FreezeColumnCount { get; set; } = 0;
1415
public bool EnableConvertByteArray { get; set; } = true;
@@ -34,7 +35,7 @@ public class OpenXmlConfiguration : Configuration
3435
/// </summary>
3536
public bool EnableAutoWidth { get; set; }
3637

37-
public double MinWidth { get; set; } = 9.28515625;
38+
public double MinWidth { get; set; } = 8.42857143;
3839

3940
public double MaxWidth { get; set; } = 200;
4041
}

0 commit comments

Comments
 (0)