mirror of
https://gitee.com/dotnetchina/MiniExcel.git
synced 2024-11-29 18:38:08 +08:00
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:
parent
228b3c180f
commit
67e97f36b5
@ -1,9 +1,4 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.OpenXml.Models;
|
||||
using MiniExcelLibs.Utils;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using MiniExcelLibs.OpenXml.Models;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml.Constants
|
||||
{
|
||||
@ -11,15 +6,13 @@ namespace MiniExcelLibs.OpenXml.Constants
|
||||
{
|
||||
static ExcelXml()
|
||||
{
|
||||
DefaultRels = MinifyXml(DefaultRels);
|
||||
DefaultWorkbookXml = MinifyXml(DefaultWorkbookXml);
|
||||
DefaultStylesXml = MinifyXml(DefaultStylesXml);
|
||||
DefaultWorkbookXmlRels = MinifyXml(DefaultWorkbookXmlRels);
|
||||
DefaultSheetRelXml = MinifyXml(DefaultSheetRelXml);
|
||||
DefaultDrawing = MinifyXml(DefaultDrawing);
|
||||
DefaultRels = ExcelOpenXmlUtils.MinifyXml( DefaultRels);
|
||||
DefaultWorkbookXml = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXml);
|
||||
DefaultWorkbookXmlRels = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXmlRels);
|
||||
DefaultSheetRelXml = ExcelOpenXmlUtils.MinifyXml(DefaultSheetRelXml);
|
||||
DefaultDrawing = ExcelOpenXmlUtils.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>";
|
||||
|
||||
@ -35,146 +28,6 @@ namespace MiniExcelLibs.OpenXml.Constants
|
||||
<Relationship Type=""http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings"" Target=""/xl/sharedStrings.xml"" Id=""R3db9602ace778fdb"" />
|
||||
</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""?>
|
||||
<x:workbook xmlns:r=""http://schemas.openxmlformats.org/officeDocument/2006/relationships""
|
||||
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)
|
||||
=> $@"<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++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.OpenXml.Constants;
|
||||
using MiniExcelLibs.OpenXml.Models;
|
||||
using MiniExcelLibs.OpenXml.Styles;
|
||||
using MiniExcelLibs.Utils;
|
||||
using MiniExcelLibs.Zip;
|
||||
using System;
|
||||
@ -364,9 +365,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
switch (_configuration.TableStyles)
|
||||
{
|
||||
case TableStyles.None:
|
||||
return ExcelXml.SetupStyleXml(ExcelXml.NoneStylesXml, columns);
|
||||
return new MinimalSheetStyleBuilder().Build( columns);
|
||||
case TableStyles.Default:
|
||||
return ExcelXml.SetupStyleXml(ExcelXml.DefaultStylesXml, columns);
|
||||
return new DefaultSheetStyleBuilder().Build( columns );
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ namespace MiniExcelLibs.OpenXml
|
||||
}
|
||||
}
|
||||
|
||||
private List<XRowInfo> XRowInfos { get; set; }
|
||||
private List<XRowInfo> XRowInfos { get; set; }
|
||||
|
||||
private readonly List<string> CalcChainCellRefs = new List<string>();
|
||||
|
||||
@ -691,9 +691,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
var mergeBaseRowIndex = newRowIndex;
|
||||
newRowIndex += rowInfo.IEnumerableMercell?.Height ?? 1;
|
||||
|
||||
// replace formulas
|
||||
ProcessFormulas( rowXml, newRowIndex );
|
||||
writer.Write(CleanXml( rowXml, endPrefix)); // pass StringBuilder for netcoreapp3.0 or above
|
||||
// replace formulas
|
||||
ProcessFormulas( rowXml, newRowIndex );
|
||||
writer.Write(CleanXml( rowXml, endPrefix)); // pass StringBuilder for netcoreapp3.0 or above
|
||||
|
||||
//mergecells
|
||||
if (rowInfo.RowMercells != null)
|
||||
@ -757,9 +757,9 @@ namespace MiniExcelLibs.OpenXml
|
||||
.Replace($"{{{{$enumrowend}}}}", enumrowend.ToString())
|
||||
.AppendFormat("</{0}>", row.Name);
|
||||
|
||||
ProcessFormulas( rowXml, newRowIndex );
|
||||
ProcessFormulas( rowXml, newRowIndex );
|
||||
|
||||
writer.Write(CleanXml( rowXml, endPrefix)); // pass StringBuilder for netcoreapp3.0 or above
|
||||
writer.Write(CleanXml( rowXml, endPrefix)); // pass StringBuilder for netcoreapp3.0 or above
|
||||
|
||||
//mergecells
|
||||
if (rowInfo.RowMercells != null)
|
||||
@ -805,48 +805,48 @@ namespace MiniExcelLibs.OpenXml
|
||||
return;
|
||||
}
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings { NameTable = _ns.NameTable };
|
||||
XmlParserContext context = new XmlParserContext( null, _ns, "", XmlSpace.Default );
|
||||
XmlReader reader = XmlReader.Create( new StringReader( rowXmlString ), settings, context );
|
||||
XmlReaderSettings settings = new XmlReaderSettings { NameTable = _ns.NameTable };
|
||||
XmlParserContext context = new XmlParserContext( null, _ns, "", XmlSpace.Default );
|
||||
XmlReader reader = XmlReader.Create( new StringReader( rowXmlString ), settings, context );
|
||||
|
||||
XmlDocument d = new XmlDocument();
|
||||
d.Load( reader );
|
||||
|
||||
var row = d.FirstChild as XmlElement;
|
||||
|
||||
// convert cells starting with '$=' into formulas
|
||||
var cs = row.SelectNodes( $"x:c", _ns );
|
||||
for ( var ci = 0; ci < cs.Count; ci++ )
|
||||
// convert cells starting with '$=' into formulas
|
||||
var cs = row.SelectNodes( $"x:c", _ns );
|
||||
for ( var ci = 0; ci < cs.Count; ci++ )
|
||||
{
|
||||
var c = cs.Item( ci ) as XmlElement;
|
||||
if ( c == null ) {
|
||||
continue;
|
||||
}
|
||||
/* Target:
|
||||
<c r="C8" s="3">
|
||||
<f>SUM(C2:C7)</f>
|
||||
</c>
|
||||
*/
|
||||
var vs = c.SelectNodes( $"x:v", _ns );
|
||||
foreach ( XmlElement v in vs )
|
||||
var c = cs.Item( ci ) as XmlElement;
|
||||
if ( c == null ) {
|
||||
continue;
|
||||
}
|
||||
/* Target:
|
||||
<c r="C8" s="3">
|
||||
<f>SUM(C2:C7)</f>
|
||||
</c>
|
||||
*/
|
||||
var vs = c.SelectNodes( $"x:v", _ns );
|
||||
foreach ( XmlElement v in vs )
|
||||
{
|
||||
if ( !v.InnerText.StartsWith( "$=" ) )
|
||||
if ( !v.InnerText.StartsWith( "$=" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var fNode = c.OwnerDocument.CreateElement( "f", Config.SpreadsheetmlXmlns );
|
||||
fNode.InnerText = v.InnerText.Substring( 2 );
|
||||
c.InsertBefore( fNode, v );
|
||||
c.RemoveChild( v );
|
||||
continue;
|
||||
}
|
||||
var fNode = c.OwnerDocument.CreateElement( "f", Config.SpreadsheetmlXmlns );
|
||||
fNode.InnerText = v.InnerText.Substring( 2 );
|
||||
c.InsertBefore( fNode, v );
|
||||
c.RemoveChild( v );
|
||||
|
||||
var celRef = ExcelOpenXmlUtils.ConvertXyToCell( ci + 1, rowIndex );
|
||||
CalcChainCellRefs.Add( celRef );
|
||||
var celRef = ExcelOpenXmlUtils.ConvertXyToCell( ci + 1, rowIndex );
|
||||
CalcChainCellRefs.Add( celRef );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
rowXml.Clear();
|
||||
rowXml.Append( row.OuterXml );
|
||||
}
|
||||
rowXml.Append( row.OuterXml );
|
||||
}
|
||||
|
||||
private static string ConvertToDateTimeString(KeyValuePair<string, PropInfo> propInfo, object cellValue)
|
||||
{
|
||||
|
@ -9,6 +9,8 @@
|
||||
#endif
|
||||
static class ExcelOpenXmlUtils
|
||||
{
|
||||
public static string MinifyXml( string xml ) => xml.Replace( "\r", "" ).Replace( "\n", "" ).Replace( "\t", "" ).Trim();
|
||||
|
||||
/// <summary>
|
||||
/// Encode to XML (special characteres: ' " > < &)
|
||||
/// </summary>
|
||||
|
156
src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs
Normal file
156
src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
10
src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs
Normal file
10
src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using MiniExcelLibs.Attributes;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MiniExcelLibs.OpenXml.Styles {
|
||||
public interface ISheetStyleBuilder
|
||||
{
|
||||
string Build( ICollection<ExcelColumnAttribute> columns );
|
||||
}
|
||||
|
||||
}
|
71
src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs
Normal file
71
src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
src/MiniExcel/OpenXml/Styles/SheetStyleBuilderHelper.cs
Normal file
24
src/MiniExcel/OpenXml/Styles/SheetStyleBuilderHelper.cs
Normal 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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using Dapper;
|
||||
using Dapper;
|
||||
using MiniExcelLibs.Attributes;
|
||||
using MiniExcelLibs.Csv;
|
||||
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 );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -29,10 +29,10 @@ namespace MiniExcelLibs.Tests
|
||||
string path = GetTempXlsxPath();
|
||||
char[] chars = new char[] {'\u0000','\u0001','\u0002','\u0003','\u0004','\u0005','\u0006','\u0007','\u0008',
|
||||
'\u0009', //<HT>
|
||||
'\u000A', //<LF>
|
||||
'\u000B','\u000C',
|
||||
'\u000A', //<LF>
|
||||
'\u000B','\u000C',
|
||||
'\u000D', //<CR>
|
||||
'\u000E','\u000F','\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016',
|
||||
'\u000E','\u000F','\u0010','\u0011','\u0012','\u0013','\u0014','\u0015','\u0016',
|
||||
'\u0017','\u0018','\u0019','\u001A','\u001B','\u001C','\u001D','\u001E','\u001F','\u007F'
|
||||
};
|
||||
var input = chars.Select(s => new { Test = s.ToString() });
|
||||
|
Loading…
Reference in New Issue
Block a user