Fix Issue 632, refactor sheet styles (#640)

* fix for issue 606

* fix formatting

* add test

* refactor sheet styles, fix #632

* change tabs to spaces
This commit is contained in:
meld-cp 2024-07-23 01:32:04 +12:00 committed by GitHub
parent 228b3c180f
commit 67e97f36b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 355 additions and 241 deletions

View File

@ -1,9 +1,4 @@
using MiniExcelLibs.Attributes; using MiniExcelLibs.OpenXml.Models;
using MiniExcelLibs.OpenXml.Models;
using MiniExcelLibs.Utils;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiniExcelLibs.OpenXml.Constants namespace MiniExcelLibs.OpenXml.Constants
{ {
@ -11,15 +6,13 @@ namespace MiniExcelLibs.OpenXml.Constants
{ {
static ExcelXml() static ExcelXml()
{ {
DefaultRels = MinifyXml(DefaultRels); DefaultRels = ExcelOpenXmlUtils.MinifyXml( DefaultRels);
DefaultWorkbookXml = MinifyXml(DefaultWorkbookXml); DefaultWorkbookXml = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXml);
DefaultStylesXml = MinifyXml(DefaultStylesXml); DefaultWorkbookXmlRels = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXmlRels);
DefaultWorkbookXmlRels = MinifyXml(DefaultWorkbookXmlRels); DefaultSheetRelXml = ExcelOpenXmlUtils.MinifyXml(DefaultSheetRelXml);
DefaultSheetRelXml = MinifyXml(DefaultSheetRelXml); DefaultDrawing = ExcelOpenXmlUtils.MinifyXml(DefaultDrawing);
DefaultDrawing = MinifyXml(DefaultDrawing);
} }
private static string MinifyXml(string xml) => xml.Replace("\r", "").Replace("\n", "").Replace("\t", "");
internal static readonly string EmptySheetXml = $@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main""><x:dimension ref=""A1""/><x:sheetData></x:sheetData></x:worksheet>"; internal static readonly string EmptySheetXml = $@"<?xml version=""1.0"" encoding=""utf-8""?><x:worksheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main""><x:dimension ref=""A1""/><x:sheetData></x:sheetData></x:worksheet>";
@ -35,146 +28,6 @@ namespace MiniExcelLibs.OpenXml.Constants
<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"" Target=""/xl/sharedStrings.xml"" Id=""R3db9602ace778fdb"" /> <Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"" Target=""/xl/sharedStrings.xml"" Id=""R3db9602ace778fdb"" />
</Relationships>"; </Relationships>";
internal static readonly string NoneStylesXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<x:styleSheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
<x:fonts>
<x:font />
</x:fonts>
<x:fills>
<x:fill />
</x:fills>
<x:borders>
<x:border />
</x:borders>
<x:cellStyleXfs>
<x:xf />
</x:cellStyleXfs>
<x:cellXfs>
<x:xf />
<x:xf />
<x:xf />
<x:xf numFmtId=""14"" applyNumberFormat=""1"" />
</x:cellXfs>
</x:styleSheet>";
#region StyleSheet
private const int startUpNumFmts = 1;
private const string NumFmtsToken = "{{numFmts}}";
private const string NumFmtsCountToken = "{{numFmtCount}}";
private const int startUpCellXfs = 5;
private const string cellXfsToken = "{{cellXfs}}";
private const string cellXfsCountToken = "{{cellXfsCount}}";
internal static readonly string DefaultStylesXml = $@"<?xml version=""1.0"" encoding=""utf-8""?>
<x:styleSheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
<x:numFmts count=""{NumFmtsCountToken}"">
<x:numFmt numFmtId=""0"" formatCode="""" />
{NumFmtsToken}
</x:numFmts>
<x:fonts count=""2"">
<x:font>
<x:vertAlign val=""baseline"" />
<x:sz val=""11"" />
<x:color rgb=""FF000000"" />
<x:name val=""Calibri"" />
<x:family val=""2"" />
</x:font>
<x:font>
<x:vertAlign val=""baseline"" />
<x:sz val=""11"" />
<x:color rgb=""FFFFFFFF"" />
<x:name val=""Calibri"" />
<x:family val=""2"" />
</x:font>
</x:fonts>
<x:fills count=""3"">
<x:fill>
<x:patternFill patternType=""none"" />
</x:fill>
<x:fill>
<x:patternFill patternType=""gray125"" />
</x:fill>
<x:fill>
<x:patternFill patternType=""solid"">
<x:fgColor rgb=""284472C4"" />
</x:patternFill>
</x:fill>
</x:fills>
<x:borders count=""2"">
<x:border diagonalUp=""0"" diagonalDown=""0"">
<x:left style=""none"">
<x:color rgb=""FF000000"" />
</x:left>
<x:right style=""none"">
<x:color rgb=""FF000000"" />
</x:right>
<x:top style=""none"">
<x:color rgb=""FF000000"" />
</x:top>
<x:bottom style=""none"">
<x:color rgb=""FF000000"" />
</x:bottom>
<x:diagonal style=""none"">
<x:color rgb=""FF000000"" />
</x:diagonal>
</x:border>
<x:border diagonalUp=""0"" diagonalDown=""0"">
<x:left style=""thin"">
<x:color rgb=""FF000000"" />
</x:left>
<x:right style=""thin"">
<x:color rgb=""FF000000"" />
</x:right>
<x:top style=""thin"">
<x:color rgb=""FF000000"" />
</x:top>
<x:bottom style=""thin"">
<x:color rgb=""FF000000"" />
</x:bottom>
<x:diagonal style=""none"">
<x:color rgb=""FF000000"" />
</x:diagonal>
</x:border>
</x:borders>
<x:cellStyleXfs count=""3"">
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""0"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""14"" fontId=""1"" fillId=""2"" borderId=""1"" applyNumberFormat=""1"" applyFill=""0"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
</x:cellStyleXfs>
<x:cellXfs count=""{cellXfsCountToken}"">
<x:xf></x:xf>
<x:xf numFmtId=""0"" fontId=""1"" fillId=""2"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""0"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""left"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""14"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyBorder=""1"" applyAlignment=""1"">
<x:alignment horizontal=""fill""/>
</x:xf>
{cellXfsToken}
</x:cellXfs>
<x:cellStyles count=""1"">
<x:cellStyle name=""Normal"" xfId=""0"" builtinId=""0"" />
</x:cellStyles>
</x:styleSheet>";
#endregion
internal static readonly string DefaultWorkbookXml = @"<?xml version=""1.0"" encoding=""utf-8""?> internal static readonly string DefaultWorkbookXml = @"<?xml version=""1.0"" encoding=""utf-8""?>
<x:workbook xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships"" <x:workbook xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships""
xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main""> xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
@ -251,51 +104,6 @@ namespace MiniExcelLibs.OpenXml.Constants
internal static string Sheet(SheetDto sheetDto, int sheetId) internal static string Sheet(SheetDto sheetDto, int sheetId)
=> $@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(sheetDto.Name)}"" sheetId=""{sheetId}""{(string.IsNullOrWhiteSpace(sheetDto.State) ? string.Empty : $" state=\"{sheetDto.State}\"")} r:id=""{sheetDto.ID}"" />"; => $@"<x:sheet name=""{ExcelOpenXmlUtils.EncodeXML(sheetDto.Name)}"" sheetId=""{sheetId}""{(string.IsNullOrWhiteSpace(sheetDto.State) ? string.Empty : $" state=\"{sheetDto.State}\"")} r:id=""{sheetDto.ID}"" />";
internal static string SetupStyleXml(string styleXml, ICollection<ExcelColumnAttribute> columns)
{
const int numFmtIndex = 166;
var sb = new StringBuilder(styleXml);
var columnsToApply = GenerateStyleIds(columns);
var numFmts = columnsToApply.Select((x, i) =>
{
return new
{
numFmt =
$@"<x:numFmt numFmtId=""{numFmtIndex + i}"" formatCode=""{x.Format}"" />",
cellXfs =
$@"<x:xf numFmtId=""{numFmtIndex + i}"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>"
};
}).ToArray();
sb.Replace(NumFmtsToken, string.Join(string.Empty, numFmts.Select(x => x.numFmt)));
sb.Replace(NumFmtsCountToken, (startUpNumFmts + numFmts.Length).ToString());
sb.Replace(cellXfsToken, string.Join(string.Empty, numFmts.Select(x => x.cellXfs)));
sb.Replace(cellXfsCountToken, (5 + numFmts.Length).ToString());
return sb.ToString();
}
private static IEnumerable<ExcelColumnAttribute> GenerateStyleIds(ICollection<ExcelColumnAttribute> dynamicColumns)
{
if (dynamicColumns == null)
yield break;
int index = 0;
foreach (var g in dynamicColumns?.Where(x => !string.IsNullOrWhiteSpace(x.Format) && new ExcelNumberFormat(x.Format).IsValid).GroupBy(x => x.Format))
{
foreach (var col in g)
col.FormatId = startUpCellXfs + index;
yield return g.First();
index++;
}
} }
} }
}

View File

@ -1,6 +1,7 @@
using MiniExcelLibs.Attributes; using MiniExcelLibs.Attributes;
using MiniExcelLibs.OpenXml.Constants; using MiniExcelLibs.OpenXml.Constants;
using MiniExcelLibs.OpenXml.Models; using MiniExcelLibs.OpenXml.Models;
using MiniExcelLibs.OpenXml.Styles;
using MiniExcelLibs.Utils; using MiniExcelLibs.Utils;
using MiniExcelLibs.Zip; using MiniExcelLibs.Zip;
using System; using System;
@ -364,9 +365,9 @@ namespace MiniExcelLibs.OpenXml
switch (_configuration.TableStyles) switch (_configuration.TableStyles)
{ {
case TableStyles.None: case TableStyles.None:
return ExcelXml.SetupStyleXml(ExcelXml.NoneStylesXml, columns); return new MinimalSheetStyleBuilder().Build( columns);
case TableStyles.Default: case TableStyles.Default:
return ExcelXml.SetupStyleXml(ExcelXml.DefaultStylesXml, columns); return new DefaultSheetStyleBuilder().Build( columns );
default: default:
return string.Empty; return string.Empty;
} }

View File

@ -9,6 +9,8 @@
#endif #endif
static class ExcelOpenXmlUtils static class ExcelOpenXmlUtils
{ {
public static string MinifyXml( string xml ) => xml.Replace( "\r", "" ).Replace( "\n", "" ).Replace( "\t", "" ).Trim();
/// <summary> /// <summary>
/// Encode to XML (special characteres: &apos; &quot; &gt; &lt; &amp;) /// Encode to XML (special characteres: &apos; &quot; &gt; &lt; &amp;)
/// </summary> /// </summary>

View File

@ -0,0 +1,156 @@
using MiniExcelLibs.Attributes;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiniExcelLibs.OpenXml.Styles {
public class DefaultSheetStyleBuilder : ISheetStyleBuilder
{
private const int startUpNumFmts = 1;
private const string NumFmtsToken = "{{numFmts}}";
private const string NumFmtsCountToken = "{{numFmtCount}}";
private const int startUpCellXfs = 5;
private const string cellXfsToken = "{{cellXfs}}";
private const string cellXfsCountToken = "{{cellXfsCount}}";
internal static readonly string DefaultStylesXml = ExcelOpenXmlUtils.MinifyXml
( $@"
<?xml version=""1.0"" encoding=""utf-8""?>
<x:styleSheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
<x:numFmts count=""{NumFmtsCountToken}"">
<x:numFmt numFmtId=""0"" formatCode="""" />
{NumFmtsToken}
</x:numFmts>
<x:fonts count=""2"">
<x:font>
<x:vertAlign val=""baseline"" />
<x:sz val=""11"" />
<x:color rgb=""FF000000"" />
<x:name val=""Calibri"" />
<x:family val=""2"" />
</x:font>
<x:font>
<x:vertAlign val=""baseline"" />
<x:sz val=""11"" />
<x:color rgb=""FFFFFFFF"" />
<x:name val=""Calibri"" />
<x:family val=""2"" />
</x:font>
</x:fonts>
<x:fills count=""3"">
<x:fill>
<x:patternFill patternType=""none"" />
</x:fill>
<x:fill>
<x:patternFill patternType=""gray125"" />
</x:fill>
<x:fill>
<x:patternFill patternType=""solid"">
<x:fgColor rgb=""284472C4"" />
</x:patternFill>
</x:fill>
</x:fills>
<x:borders count=""2"">
<x:border diagonalUp=""0"" diagonalDown=""0"">
<x:left style=""none"">
<x:color rgb=""FF000000"" />
</x:left>
<x:right style=""none"">
<x:color rgb=""FF000000"" />
</x:right>
<x:top style=""none"">
<x:color rgb=""FF000000"" />
</x:top>
<x:bottom style=""none"">
<x:color rgb=""FF000000"" />
</x:bottom>
<x:diagonal style=""none"">
<x:color rgb=""FF000000"" />
</x:diagonal>
</x:border>
<x:border diagonalUp=""0"" diagonalDown=""0"">
<x:left style=""thin"">
<x:color rgb=""FF000000"" />
</x:left>
<x:right style=""thin"">
<x:color rgb=""FF000000"" />
</x:right>
<x:top style=""thin"">
<x:color rgb=""FF000000"" />
</x:top>
<x:bottom style=""thin"">
<x:color rgb=""FF000000"" />
</x:bottom>
<x:diagonal style=""none"">
<x:color rgb=""FF000000"" />
</x:diagonal>
</x:border>
</x:borders>
<x:cellStyleXfs count=""3"">
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""0"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""14"" fontId=""1"" fillId=""2"" borderId=""1"" applyNumberFormat=""1"" applyFill=""0"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
</x:cellStyleXfs>
<x:cellXfs count=""{cellXfsCountToken}"">
<x:xf></x:xf>
<x:xf numFmtId=""0"" fontId=""1"" fillId=""2"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""0"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""left"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""14"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>
<x:xf numFmtId=""0"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyBorder=""1"" applyAlignment=""1"">
<x:alignment horizontal=""fill""/>
</x:xf>
{cellXfsToken}
</x:cellXfs>
<x:cellStyles count=""1"">
<x:cellStyle name=""Normal"" xfId=""0"" builtinId=""0"" />
</x:cellStyles>
</x:styleSheet>"
);
public string Build( ICollection<ExcelColumnAttribute> columns )
{
const int numFmtIndex = 166;
var sb = new StringBuilder( DefaultStylesXml );
var columnsToApply = SheetStyleBuilderHelper.GenerateStyleIds( startUpCellXfs, columns );
var numFmts = columnsToApply.Select( ( x, i ) =>
{
return new
{
numFmt = $@"<x:numFmt numFmtId=""{numFmtIndex + i}"" formatCode=""{x.Format}"" />",
cellXfs = $@"<x:xf numFmtId=""{numFmtIndex + i}"" fontId=""0"" fillId=""0"" borderId=""1"" xfId=""0"" applyNumberFormat=""1"" applyFill=""1"" applyBorder=""1"" applyAlignment=""1"" applyProtection=""1"">
<x:alignment horizontal=""general"" vertical=""bottom"" textRotation=""0"" wrapText=""0"" indent=""0"" relativeIndent=""0"" justifyLastLine=""0"" shrinkToFit=""0"" readingOrder=""0"" />
<x:protection locked=""1"" hidden=""0"" />
</x:xf>"
};
} ).ToArray();
sb.Replace( NumFmtsToken, string.Join( string.Empty, numFmts.Select( x => x.numFmt ) ) );
sb.Replace( NumFmtsCountToken, (startUpNumFmts + numFmts.Length).ToString() );
sb.Replace( cellXfsToken, string.Join( string.Empty, numFmts.Select( x => x.cellXfs ) ) );
sb.Replace( cellXfsCountToken, (5 + numFmts.Length).ToString() );
return sb.ToString();
}
}
}

View File

@ -0,0 +1,10 @@
using MiniExcelLibs.Attributes;
using System.Collections.Generic;
namespace MiniExcelLibs.OpenXml.Styles {
public interface ISheetStyleBuilder
{
string Build( ICollection<ExcelColumnAttribute> columns );
}
}

View File

@ -0,0 +1,71 @@
using MiniExcelLibs.Attributes;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MiniExcelLibs.OpenXml.Styles {
public class MinimalSheetStyleBuilder : ISheetStyleBuilder
{
private const int startUpNumFmts = 1;
private const string NumFmtsToken = "{{numFmts}}";
private const string NumFmtsCountToken = "{{numFmtCount}}";
private const int startUpCellXfs = 5;
private const string cellXfsToken = "{{cellXfs}}";
private const string cellXfsCountToken = "{{cellXfsCount}}";
internal static readonly string NoneStylesXml = ExcelOpenXmlUtils.MinifyXml
( $@"
<?xml version=""1.0"" encoding=""utf-8""?>
<x:styleSheet xmlns:x=""http://schemas.openxmlformats.org/spreadsheetml/2006/main"">
<x:numFmts count=""{NumFmtsCountToken}"">
<x:numFmt numFmtId=""0"" formatCode="""" />
{NumFmtsToken}
</x:numFmts>
<x:fonts>
<x:font />
</x:fonts>
<x:fills>
<x:fill />
</x:fills>
<x:borders>
<x:border />
</x:borders>
<x:cellStyleXfs>
<x:xf />
</x:cellStyleXfs>
<x:cellXfs count=""{cellXfsCountToken}"">
<x:xf />
<x:xf />
<x:xf />
<x:xf numFmtId=""14"" applyNumberFormat=""1"" />
<x:xf />
{cellXfsToken}
</x:cellXfs>
</x:styleSheet>"
);
public string Build( ICollection<ExcelColumnAttribute> columns )
{
const int numFmtIndex = 166;
var sb = new StringBuilder( NoneStylesXml );
var columnsToApply = SheetStyleBuilderHelper.GenerateStyleIds( startUpCellXfs, columns );
var numFmts = columnsToApply.Select( ( x, i ) => {
return new {
numFmt = $@"<x:numFmt numFmtId=""{numFmtIndex + i}"" formatCode=""{x.Format}"" />",
cellXfs = $@"<x:xf numFmtId=""{numFmtIndex + i}"" applyNumberFormat=""1"" />"
};
} ).ToArray();
sb.Replace( NumFmtsToken, string.Join( string.Empty, numFmts.Select( x => x.numFmt ) ) );
sb.Replace( NumFmtsCountToken, (startUpNumFmts + numFmts.Length).ToString() );
sb.Replace( cellXfsToken, string.Join( string.Empty, numFmts.Select( x => x.cellXfs ) ) );
sb.Replace( cellXfsCountToken, (5 + numFmts.Length).ToString() );
return sb.ToString();
}
}
}

View File

@ -0,0 +1,24 @@
using MiniExcelLibs.Attributes;
using MiniExcelLibs.Utils;
using System.Collections.Generic;
using System.Linq;
namespace MiniExcelLibs.OpenXml.Styles {
public static class SheetStyleBuilderHelper
{
public static IEnumerable<ExcelColumnAttribute> GenerateStyleIds( int startUpCellXfs, ICollection<ExcelColumnAttribute> dynamicColumns ) {
if ( dynamicColumns == null )
yield break;
int index = 0;
foreach ( var g in dynamicColumns?.Where( x => !string.IsNullOrWhiteSpace( x.Format ) && new ExcelNumberFormat( x.Format ).IsValid ).GroupBy( x => x.Format ) ) {
foreach ( var col in g )
col.FormatId = startUpCellXfs + index;
yield return g.First();
index++;
}
}
}
}

View File

@ -1,4 +1,4 @@
using Dapper; using Dapper;
using MiniExcelLibs.Attributes; using MiniExcelLibs.Attributes;
using MiniExcelLibs.Csv; using MiniExcelLibs.Csv;
using MiniExcelLibs.Exceptions; using MiniExcelLibs.Exceptions;
@ -3692,5 +3692,47 @@ MyProperty4,MyProperty1,MyProperty5,MyProperty2,MyProperty6,,MyProperty3
} }
[Fact]
public void Issue632_1()
{
//https://github.com/mini-software/MiniExcel/issues/632
var values = new List<Dictionary<string, object>>();
foreach ( var item in Enumerable.Range( 1, 100 ) ) {
var dict = new Dictionary<string, object>
{
{ "Id", item },
{ "Time", DateTime.Now.ToLocalTime() },
{ "CPU Usage (%)", Math.Round( 56.345, 1 ) },
{ "Memory Usage (%)", Math.Round( 98.234, 1 ) },
{ "Disk Usage (%)", Math.Round( 32.456, 1 ) },
{ "CPU Temperature (°C)", Math.Round( 74.234, 1 ) },
{ "Voltage (V)", Math.Round( 6.3223, 1 ) },
{ "Network Usage (Kb/s)", Math.Round( 4503.23422, 1 ) },
{ "Instrument", "QT800050" }
};
values.Add( dict );
}
var config = new OpenXmlConfiguration
{
TableStyles = TableStyles.None,
DynamicColumns = new DynamicExcelColumn[]
{
//new DynamicExcelColumn("Time") { Index = 0, Width = 20, Format = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern + " " + CultureInfo.CurrentCulture.DateTimeFormat.LongTimePattern },
//new DynamicExcelColumn("Time") { Index = 0, Width = 20, Format = CultureInfo.InvariantCulture.DateTimeFormat.ShortDatePattern + " " + CultureInfo.InvariantCulture.DateTimeFormat.LongTimePattern },
//new DynamicExcelColumn("Time") { Index = 0, Width = 20 },
new DynamicExcelColumn("Time") { Index = 0, Width = 20, Format = "d.MM.yyyy" },
}
};
var path = Path.Combine(
Path.GetTempPath(),
string.Concat( nameof( MiniExcelIssueTests ), "_", nameof( Issue632_1 ), ".xlsx" )
);
MiniExcel.SaveAs( path, values, excelType: ExcelType.XLSX, configuration: config, overwriteFile: true );
}
} }
} }