Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Issue 632, refactor sheet styles #640

Merged
merged 8 commits into from
Jul 22, 2024
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
206 changes: 7 additions & 199 deletions src/MiniExcel/OpenXml/Constants/ExcelXml.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
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
{
internal static class ExcelXml
{
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>";

Expand All @@ -35,146 +28,6 @@ static ExcelXml()
<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"">
Expand Down Expand Up @@ -251,51 +104,6 @@ internal static string DrawingXml(FileDto file, int fileIndex)
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++;
}
}

}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -364,9 +365,9 @@ private string GetStylesXml(ICollection<ExcelColumnAttribute> columns)
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;
}
Expand Down
76 changes: 38 additions & 38 deletions src/MiniExcel/OpenXml/ExcelOpenXmlTemplate.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public string ToXmlString(string prefix)
}
}

private List<XRowInfo> XRowInfos { get; set; }
private List<XRowInfo> XRowInfos { get; set; }

private readonly List<string> CalcChainCellRefs = new List<string>();

Expand Down Expand Up @@ -691,9 +691,9 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
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)
Expand Down Expand Up @@ -757,9 +757,9 @@ private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData, bo
.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)
Expand Down Expand Up @@ -805,48 +805,48 @@ private void ProcessFormulas( StringBuilder rowXml, int rowIndex )
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 );

var celRef = ExcelOpenXmlUtils.ConvertXyToCell( ci + 1, rowIndex );
CalcChainCellRefs.Add( celRef );

}
}
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 );

}
}
rowXml.Clear();
rowXml.Append( row.OuterXml );
}
rowXml.Append( row.OuterXml );
}

private static string ConvertToDateTimeString(KeyValuePair<string, PropInfo> propInfo, object cellValue)
{
Expand Down
2 changes: 2 additions & 0 deletions src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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: &apos; &quot; &gt; &lt; &amp;)
/// </summary>
Expand Down
Loading
Loading