|
1 | 1 | using MiniExcelLibs.Utils; |
2 | 2 | using System; |
| 3 | +using System.Collections; |
3 | 4 | using System.Collections.Generic; |
| 5 | +using System.Diagnostics; |
4 | 6 | using System.Linq; |
5 | 7 |
|
6 | 8 | namespace MiniExcelLibs.OpenXml |
7 | 9 | { |
8 | 10 | public sealed class ExcelColumnWidth |
9 | 11 | { |
| 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 | + |
10 | 18 | public int Index { get; set; } |
11 | 19 | 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 |
12 | 37 |
|
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) |
14 | 45 | { |
15 | 46 | var i = 1; |
16 | | - foreach (var p in props) |
| 47 | + var columnWidths = new List<ExcelColumnWidth>(); |
| 48 | + |
| 49 | + foreach (var map in mappings) |
17 | 50 | { |
18 | | - if (p?.ExcelColumnWidth != null || minWidth != null) |
| 51 | + if ((map?.ExcelHidden ?? false) || map?.ExcelColumnWidth != null || minWidth != null) |
19 | 52 | { |
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}); |
26 | 58 | } |
27 | 59 |
|
28 | 60 | i++; |
29 | 61 | } |
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; |
39 | 62 |
|
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); |
44 | 64 | } |
45 | 65 |
|
46 | | - public void AdjustWidth(int columnIndex, string columnValue) |
| 66 | + internal void AdjustWidth(int columnIndex, string columnValue) |
47 | 67 | { |
48 | | - if (!string.IsNullOrEmpty(columnValue) |
49 | | - && _columnWidths.TryGetValue(columnIndex, out var currentWidth)) |
| 68 | + if (!string.IsNullOrEmpty(columnValue) && _columnWidths.TryGetValue(columnIndex, out var currentWidth)) |
50 | 69 | { |
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); |
52 | 72 | currentWidth.Width = Math.Min(_maxWidth, adjustedWidth); |
53 | 73 | } |
54 | 74 | } |
55 | 75 |
|
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(); |
66 | 78 |
|
67 | | - var excelColumnWidth = (textLength * characterWidthFactor) + padding; |
68 | | - return Math.Round(excelColumnWidth, 2); |
69 | | - } |
| 79 | + public int Count => _columnWidths.Count; |
70 | 80 | } |
71 | 81 | } |
0 commit comments