diff --git a/src/MiniExcel/Csv/CsvWriter.cs b/src/MiniExcel/Csv/CsvWriter.cs index bc6d1cb5..9b4e8c70 100644 --- a/src/MiniExcel/Csv/CsvWriter.cs +++ b/src/MiniExcel/Csv/CsvWriter.cs @@ -64,6 +64,21 @@ public void SaveAs() } } + public async Task SaveAsAsync(CancellationToken cancellationToken = default) + { + await Task.Run(() => SaveAs(), cancellationToken).ConfigureAwait(false); + } + + public void Insert(bool overwriteSheet = false) + { + SaveAs(); + } + + public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + await Task.Run(() => SaveAs(), cancellationToken).ConfigureAwait(false); + } + private void GenerateSheetByIEnumerable(IEnumerable values, string seperator, string newLine, StreamWriter writer) { Type genericType = null; @@ -130,16 +145,6 @@ private void GenerateSheetByIEnumerable(IEnumerable values, string seperator, st } } - public void Insert() - { - SaveAs(); - } - - public async Task SaveAsAsync(CancellationToken cancellationToken = default(CancellationToken)) - { - await Task.Run(() => SaveAs(), cancellationToken).ConfigureAwait(false); - } - private void GenerateSheetByIDataReader(IDataReader reader, string seperator, string newLine, StreamWriter writer) { int fieldCount = reader.FieldCount; diff --git a/src/MiniExcel/IExcelWriter.cs b/src/MiniExcel/IExcelWriter.cs index b8fb6d05..be5195db 100644 --- a/src/MiniExcel/IExcelWriter.cs +++ b/src/MiniExcel/IExcelWriter.cs @@ -6,7 +6,8 @@ namespace MiniExcelLibs internal interface IExcelWriter { void SaveAs(); - Task SaveAsAsync(CancellationToken cancellationToken = default(CancellationToken)); - void Insert(); + Task SaveAsAsync(CancellationToken cancellationToken = default); + void Insert(bool overwriteSheet = false); + Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default); } } diff --git a/src/MiniExcel/MiniExcel.Async.cs b/src/MiniExcel/MiniExcel.Async.cs index 8cf080d6..342dc0fb 100644 --- a/src/MiniExcel/MiniExcel.Async.cs +++ b/src/MiniExcel/MiniExcel.Async.cs @@ -1,15 +1,67 @@ namespace MiniExcelLibs { + using MiniExcelLibs.OpenXml; using System; + using System.Collections; using System.Collections.Generic; using System.Data; using System.IO; + using System.Linq; using System.Threading; using System.Threading.Tasks; using Utils; public static partial class MiniExcel { + public static async Task InsertAsync(string path, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") + throw new NotSupportedException("MiniExcel Insert not support xlsm"); + + if (!File.Exists(path)) + { + await SaveAsAsync(path, value, printHeader, sheetName, excelType, cancellationToken: cancellationToken); + } + else + { + if (excelType == ExcelType.CSV) + { + using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) + await InsertAsync(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet, cancellationToken); + } + else + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.SequentialScan)) + await InsertAsync(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet, cancellationToken); + } + } + } + + public static async Task InsertAsync(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + stream.Seek(0, SeekOrigin.End); + // reuse code + if (excelType == ExcelType.CSV) + { + object v = null; + { + if (!(value is IEnumerable) && !(value is IDataReader) && !(value is IDictionary) && !(value is IDictionary)) + v = Enumerable.Range(0, 1).Select(s => value); + else + v = value; + } + await ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).InsertAsync(overwriteSheet); + } + else + { + if (configuration == null) + { + configuration = new OpenXmlConfiguration { FastMode = true }; + } + await ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).InsertAsync(overwriteSheet); + } + } + public static async Task SaveAsAsync(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool overwriteFile = false, CancellationToken cancellationToken = default(CancellationToken)) { if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") diff --git a/src/MiniExcel/MiniExcel.cs b/src/MiniExcel/MiniExcel.cs index cef8a08c..44686038 100644 --- a/src/MiniExcel/MiniExcel.cs +++ b/src/MiniExcel/MiniExcel.cs @@ -24,31 +24,53 @@ public static MiniExcelDataReader GetReader(this Stream stream, bool useHeaderRo return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); } - public static void Insert(string path, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null) + public static void Insert(string path, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false) { - if (Path.GetExtension(path).ToLowerInvariant() != ".csv") - throw new NotSupportedException("MiniExcel only support csv insert now"); + if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") + throw new NotSupportedException("MiniExcel Insert not support xlsm"); - using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) - Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration); + if (!File.Exists(path)) + { + SaveAs(path, value, printHeader, sheetName, excelType); + } + else + { + if (excelType == ExcelType.CSV) + { + using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) + Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet); + } + else + { + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.SequentialScan)) + Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet); + } + } } - public static void Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + public static void Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false) { - if (excelType != ExcelType.CSV) - throw new NotSupportedException("MiniExcel only support csv insert now"); - + stream.Seek(0, SeekOrigin.End); // reuse code - object v = null; + if (excelType == ExcelType.CSV) { - if (!(value is IEnumerable) && !(value is IDataReader) && !(value is IDictionary) && !(value is IDictionary)) - v = Enumerable.Range(0, 1).Select(s => value); - else - v = value; + object v = null; + { + if (!(value is IEnumerable) && !(value is IDataReader) && !(value is IDictionary) && !(value is IDictionary)) + v = Enumerable.Range(0, 1).Select(s => value); + else + v = value; + } + ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).Insert(overwriteSheet); + } + else + { + if (configuration == null) + { + configuration = new OpenXmlConfiguration { FastMode = true }; + } + ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).Insert(overwriteSheet); } - - stream.Seek(0, SeekOrigin.End); - ExcelWriterFactory.GetProvider(stream, v, sheetName, excelType, configuration, false).Insert(); } public static void SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool overwriteFile = false) diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs index 5c0b08ca..d0b7fab1 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs @@ -1,4 +1,6 @@ using MiniExcelLibs.OpenXml.Constants; +using MiniExcelLibs.OpenXml.Models; +using MiniExcelLibs.OpenXml.Styles; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; using System; @@ -15,7 +17,7 @@ namespace MiniExcelLibs.OpenXml { internal partial class ExcelOpenXmlSheetWriter : IExcelWriter { - public async Task SaveAsAsync(CancellationToken cancellationToken = default(CancellationToken)) + public async Task SaveAsAsync(CancellationToken cancellationToken = default) { await GenerateDefaultOpenXmlAsync(cancellationToken); @@ -32,6 +34,67 @@ internal partial class ExcelOpenXmlSheetWriter : IExcelWriter _archive.Dispose(); } + public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + if (!_configuration.FastMode) + { + throw new InvalidOperationException("Insert requires fast mode to be enabled"); + } + + var sheetRecords = new ExcelOpenXmlSheetReader(_stream, _configuration).GetWorkbookRels(_archive.Entries).ToArray(); + foreach (var sheetRecord in sheetRecords.OrderBy(o => o.Id)) + { + _sheets.Add(new SheetDto { Name = sheetRecord.Name, SheetIdx = (int)sheetRecord.Id, State = sheetRecord.State }); + } + var existSheetDto = _sheets.SingleOrDefault(s => s.Name == _defaultSheetName); + if (existSheetDto != null && !overwriteSheet) + { + throw new Exception($"Sheet “{_defaultSheetName}” already exist"); + } + + await GenerateStylesXmlAsync(cancellationToken);//GenerateStylesXml必须在校验overwriteSheet之后,避免不必要的样式更改 + + if (existSheetDto == null) + { + currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; + var insertSheetInfo = GetSheetInfos(_defaultSheetName); + var insertSheetDto = insertSheetInfo.ToDto(currentSheetIndex); + _sheets.Add(insertSheetDto); + await CreateSheetXmlAsync(_value, insertSheetDto.Path, cancellationToken); + } + else + { + currentSheetIndex = existSheetDto.SheetIdx; + _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(currentSheetIndex))?.Delete(); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(currentSheetIndex))?.Delete(); + await CreateSheetXmlAsync(_value, existSheetDto.Path, cancellationToken); + } + + await AddFilesToZipAsync(cancellationToken); + + await GenerateDrawinRelXmlAsync(currentSheetIndex, cancellationToken); + + await GenerateDrawingXmlAsync(currentSheetIndex, cancellationToken); + + GenerateWorkBookXmls(out StringBuilder workbookXml, out StringBuilder workbookRelsXml, out Dictionary sheetsRelsXml); + + foreach (var sheetRelsXml in sheetsRelsXml) + { + var sheetRelsXmlPath = ExcelFileNames.SheetRels(sheetRelsXml.Key); + _archive.Entries.SingleOrDefault(s => s.FullName == sheetRelsXmlPath)?.Delete(); + await CreateZipEntryAsync(sheetRelsXmlPath, null, ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value), cancellationToken); + } + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Workbook)?.Delete(); + await CreateZipEntryAsync(ExcelFileNames.Workbook, ExcelContentTypes.Workbook, ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()), cancellationToken); + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.WorkbookRels)?.Delete(); + await CreateZipEntryAsync(ExcelFileNames.WorkbookRels, null, ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()), cancellationToken); + + _archive.Dispose(); + } + internal async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationToken) { await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken); @@ -589,7 +652,7 @@ private async Task WriteColumnsWidthsAsync(MiniExcelAsyncStreamWriter writer, IE await writer.WriteAsync(WorksheetXml.EndCols); } - private static async Task PrintHeaderAsync(MiniExcelAsyncStreamWriter writer, List props) + private async Task PrintHeaderAsync(MiniExcelAsyncStreamWriter writer, List props) { var xIndex = 1; var yIndex = 1; @@ -658,9 +721,9 @@ private async Task GenerateSheetByColumnInfoAsync(MiniExcelAsyncStreamWr return yIndex - 1; } - private static async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, string cellReference, string columnName) + private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, string cellReference, string columnName) { - await writer.WriteAsync(WorksheetXml.Cell(cellReference, "str", "1", ExcelOpenXmlUtils.EncodeXML(columnName))); + await writer.WriteAsync(WorksheetXml.Cell(cellReference, "str", GetCellXfId("1"), ExcelOpenXmlUtils.EncodeXML(columnName))); } private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo p, ExcelWidthCollection widthCollection) @@ -670,7 +733,7 @@ private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, int rowInde if (_configuration.EnableWriteNullValueCell && valueIsNull) { - await writer.WriteAsync(WorksheetXml.EmptyCell(columnReference, "2")); + await writer.WriteAsync(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); return; } @@ -697,7 +760,7 @@ private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, int rowInde } } - await writer.WriteAsync(WorksheetXml.Cell(columnReference, dataType, styleIndex, cellValue, preserveSpace: preserveSpace, columnType: columnType)); + await writer.WriteAsync(WorksheetXml.Cell(columnReference, dataType, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); widthCollection?.AdjustWidth(cellIndex, cellValue); } @@ -727,41 +790,59 @@ private async Task AddFilesToZipAsync(CancellationToken cancellationToken) /// private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken) { - var styleXml = GetStylesXml(_configuration.DynamicColumns); - - await CreateZipEntryAsync( - ExcelFileNames.Styles, - ExcelContentTypes.Styles, - styleXml, - cancellationToken); + using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) + { + var builder = (ISheetStyleBuilder)null; + switch (_configuration.TableStyles) + { + case TableStyles.None: + builder = new MinimalSheetStyleBuilder(context); + break; + case TableStyles.Default: + builder = new DefaultSheetStyleBuilder(context); + break; + } + var result = await builder.BuildAsync(cancellationToken); + cellXfIdMap = result.CellXfIdMap; + } } private async Task GenerateDrawinRelXmlAsync(CancellationToken cancellationToken) { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingRelationshipXml(sheetIndex); - await CreateZipEntryAsync( - ExcelFileNames.DrawingRels(sheetIndex), - string.Empty, - ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing), - cancellationToken); + await GenerateDrawinRelXmlAsync(sheetIndex, cancellationToken); } } + private async Task GenerateDrawinRelXmlAsync(int sheetIndex, CancellationToken cancellationToken) + { + var drawing = GetDrawingRelationshipXml(sheetIndex); + await CreateZipEntryAsync( + ExcelFileNames.DrawingRels(sheetIndex), + string.Empty, + ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing), + cancellationToken); + } + private async Task GenerateDrawingXmlAsync(CancellationToken cancellationToken) { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingXml(sheetIndex); - await CreateZipEntryAsync( - ExcelFileNames.Drawing(sheetIndex), - ExcelContentTypes.Drawing, - ExcelXml.DefaultDrawing.Replace("{{format}}", drawing), - cancellationToken); + await GenerateDrawingXmlAsync(sheetIndex, cancellationToken); } } + private async Task GenerateDrawingXmlAsync(int sheetIndex, CancellationToken cancellationToken) + { + var drawing = GetDrawingXml(sheetIndex); + await CreateZipEntryAsync( + ExcelFileNames.Drawing(sheetIndex), + ExcelContentTypes.Drawing, + ExcelXml.DefaultDrawing.Replace("{{format}}", drawing), + cancellationToken); + } + /// /// workbook.xml 、 workbookRelsXml /// diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs index ccffa8de..fc1d65ff 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs @@ -16,12 +16,8 @@ namespace MiniExcelLibs.OpenXml { internal partial class ExcelOpenXmlSheetWriter : IExcelWriter { - public void Insert() - { - throw new NotImplementedException(); - } - private readonly Dictionary _zipDictionary = new Dictionary(); + private Dictionary cellXfIdMap; private IEnumerable> GetSheets() { @@ -83,7 +79,7 @@ private ExcellSheetInfo GetSheetInfos(string sheetName) return info; } - + private string GetSheetViews() { // exit early if no style to write @@ -192,7 +188,7 @@ private ExcelColumnInfo GetColumnInfosFromDynamicConfiguration(string columnName if (_configuration.DynamicColumns == null || _configuration.DynamicColumns.Length <= 0) return prop; - var dynamicColumn = _configuration.DynamicColumns.SingleOrDefault(_ => string.Equals(_.Key , columnName,StringComparison.OrdinalIgnoreCase)); + var dynamicColumn = _configuration.DynamicColumns.SingleOrDefault(_ => string.Equals(_.Key, columnName, StringComparison.OrdinalIgnoreCase)); if (dynamicColumn == null || dynamicColumn.Ignore) { return prop; @@ -237,7 +233,7 @@ private void SetGenericTypePropertiesMode(Type genericType, ref string mode, out { throw new NotImplementedException($"MiniExcel not support only {genericType.Name} value generic type"); } - + if (genericType == typeof(string) || genericType == typeof(DateTime) || genericType == typeof(Guid)) { throw new NotImplementedException($"MiniExcel not support only {genericType.Name} generic type"); @@ -261,7 +257,7 @@ private Tuple GetCellValue(int rowIndex, int cellIndex, } var type = GetValueType(value, columnInfo); - + if (columnInfo?.ExcelFormat != null && columnInfo?.ExcelFormatId == -1 && value is IFormattable formattableValue) { @@ -285,7 +281,7 @@ private Tuple GetCellValue(int rowIndex, int cellIndex, var description = CustomPropertyHelper.DescriptionAttr(type, value); return Tuple.Create("2", "str", description ?? value.ToString()); } - + if (TypeHelper.IsNumericType(type)) { var dataType = _configuration.Culture == CultureInfo.InvariantCulture ? "n" : "str"; @@ -295,7 +291,7 @@ private Tuple GetCellValue(int rowIndex, int cellIndex, return Tuple.Create("2", dataType, cellValue); } - + if (type == typeof(bool)) { return Tuple.Create("2", "b", (bool)value ? "1" : "0"); @@ -334,42 +330,42 @@ private string GetNumericValue(object value, Type type) { return ((decimal)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(int))) { return ((int)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(double))) { return ((double)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(long))) { return ((long)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(uint))) { return ((uint)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(ushort))) { return ((ushort)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(ulong))) { return ((ulong)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(short))) { return ((short)value).ToString(_configuration.Culture); } - + if (type.IsAssignableFrom(typeof(float))) { return ((float)value).ToString(_configuration.Culture); @@ -459,19 +455,6 @@ private string GetDimensionRef(int maxRowIndex, int maxColumnIndex) return dimensionRef; } - private string GetStylesXml(ICollection columns) - { - switch (_configuration.TableStyles) - { - case TableStyles.None: - return new MinimalSheetStyleBuilder().Build( columns); - case TableStyles.Default: - return new DefaultSheetStyleBuilder().Build( columns ); - default: - return string.Empty; - } - } - private string GetDrawingRelationshipXml(int sheetIndex) { var drawing = new StringBuilder(); @@ -488,7 +471,7 @@ private string GetDrawingXml(int sheetIndex) var drawing = new StringBuilder(); for (int fileIndex = 0; fileIndex < _files.Count; fileIndex++) - { + { var file = _files[fileIndex]; if (file.IsImage && file.SheetId == sheetIndex + 1) { @@ -532,5 +515,14 @@ private string GetContentTypesXml() sb.Append(ExcelXml.EndTypes); return sb.ToString(); } + + private string GetCellXfId(string styleIndex) + { + if (cellXfIdMap.TryGetValue(styleIndex, out var cellXfId)) + { + return cellXfId.ToString(); + } + return styleIndex.ToString(); + } } } diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index f04606c5..1dee3a4d 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -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; @@ -11,7 +12,6 @@ using System.IO.Compression; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace MiniExcelLibs.OpenXml { @@ -69,6 +69,67 @@ public void SaveAs() _archive.Dispose(); } + public void Insert(bool overwriteSheet = false) + { + if (!_configuration.FastMode) + { + throw new InvalidOperationException("Insert requires fast mode to be enabled"); + } + + var sheetRecords = new ExcelOpenXmlSheetReader(_stream, _configuration).GetWorkbookRels(_archive.Entries).ToArray(); + foreach (var sheetRecord in sheetRecords.OrderBy(o => o.Id)) + { + _sheets.Add(new SheetDto { Name = sheetRecord.Name, SheetIdx = (int)sheetRecord.Id, State = sheetRecord.State }); + } + var existSheetDto = _sheets.SingleOrDefault(s => s.Name == _defaultSheetName); + if (existSheetDto != null && !overwriteSheet) + { + throw new Exception($"Sheet “{_defaultSheetName}” already exist"); + } + + GenerateStylesXml();//GenerateStylesXml必须在校验overwriteSheet之后,避免不必要的样式更改 + + if (existSheetDto == null) + { + currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; + var insertSheetInfo = GetSheetInfos(_defaultSheetName); + var insertSheetDto = insertSheetInfo.ToDto(currentSheetIndex); + _sheets.Add(insertSheetDto); + CreateSheetXml(_value, insertSheetDto.Path); + } + else + { + currentSheetIndex = existSheetDto.SheetIdx; + _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(currentSheetIndex))?.Delete(); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(currentSheetIndex))?.Delete(); + CreateSheetXml(_value, existSheetDto.Path); + } + + AddFilesToZip(); + + GenerateDrawinRelXml(currentSheetIndex); + + GenerateDrawingXml(currentSheetIndex); + + GenerateWorkBookXmls(out StringBuilder workbookXml, out StringBuilder workbookRelsXml, out Dictionary sheetsRelsXml); + + foreach (var sheetRelsXml in sheetsRelsXml) + { + var sheetRelsXmlPath = ExcelFileNames.SheetRels(sheetRelsXml.Key); + _archive.Entries.SingleOrDefault(s => s.FullName == sheetRelsXmlPath)?.Delete(); + CreateZipEntry(sheetRelsXmlPath, null, ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value)); + } + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Workbook)?.Delete(); + CreateZipEntry(ExcelFileNames.Workbook, ExcelContentTypes.Workbook, ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString())); + + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.WorkbookRels)?.Delete(); + CreateZipEntry(ExcelFileNames.WorkbookRels, null, ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString())); + + _archive.Dispose(); + } + internal void GenerateDefaultOpenXml() { CreateZipEntry(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels); @@ -486,7 +547,7 @@ private void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable props) + private void PrintHeader(MiniExcelStreamWriter writer, List props) { var xIndex = 1; var yIndex = 1; @@ -561,7 +622,7 @@ private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex if (_configuration.EnableWriteNullValueCell && valueIsNull) { - writer.Write(WorksheetXml.EmptyCell(columnReference, "2")); + writer.Write(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); return; } @@ -587,12 +648,12 @@ private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex /*Prefix and suffix blank space will lost after SaveAs #294*/ var preserveSpace = cellValue != null && (cellValue.StartsWith(" ", StringComparison.Ordinal) || cellValue.EndsWith(" ", StringComparison.Ordinal)); - writer.Write(WorksheetXml.Cell(columnReference, dataType, styleIndex, cellValue, preserveSpace: preserveSpace, columnType: columnType)); + writer.Write(WorksheetXml.Cell(columnReference, dataType, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); widthCollection?.AdjustWidth(cellIndex, cellValue); } - private static void WriteCell(MiniExcelStreamWriter writer, string cellReference, string columnName) - => writer.Write(WorksheetXml.Cell(cellReference, "str", "1", ExcelOpenXmlUtils.EncodeXML(columnName))); + private void WriteCell(MiniExcelStreamWriter writer, string cellReference, string columnName) + => writer.Write(WorksheetXml.Cell(cellReference, "str", GetCellXfId("1"), ExcelOpenXmlUtils.EncodeXML(columnName))); private void GenerateEndXml() { @@ -620,35 +681,57 @@ private void AddFilesToZip() /// private void GenerateStylesXml() { - var styleXml = GetStylesXml(_configuration.DynamicColumns); - CreateZipEntry(ExcelFileNames.Styles, ExcelContentTypes.Styles, styleXml); + using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) + { + var builder = (ISheetStyleBuilder)null; + switch (_configuration.TableStyles) + { + case TableStyles.None: + builder = new MinimalSheetStyleBuilder(context); + break; + case TableStyles.Default: + builder = new DefaultSheetStyleBuilder(context); + break; + } + var result = builder.Build(); + cellXfIdMap = result.CellXfIdMap; + } } private void GenerateDrawinRelXml() { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingRelationshipXml(sheetIndex); - CreateZipEntry( - ExcelFileNames.DrawingRels(sheetIndex), - null, - ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); + GenerateDrawinRelXml(sheetIndex); } } + private void GenerateDrawinRelXml(int sheetIndex) + { + var drawing = GetDrawingRelationshipXml(sheetIndex); + CreateZipEntry( + ExcelFileNames.DrawingRels(sheetIndex), + null, + ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); + } + private void GenerateDrawingXml() { for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - var drawing = GetDrawingXml(sheetIndex); - - CreateZipEntry( - ExcelFileNames.Drawing(sheetIndex), - ExcelContentTypes.Drawing, - ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + GenerateDrawingXml(sheetIndex); } } + private void GenerateDrawingXml(int sheetIndex) + { + var drawing = GetDrawingXml(sheetIndex); + CreateZipEntry( + ExcelFileNames.Drawing(sheetIndex), + ExcelContentTypes.Drawing, + ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + } + /// /// workbook.xml、workbookRelsXml /// diff --git a/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs b/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs index 5468af0f..e8c9ed15 100644 --- a/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs +++ b/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs @@ -1,156 +1,956 @@ -using MiniExcelLibs.Attributes; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Threading.Tasks; -namespace MiniExcelLibs.OpenXml.Styles { - - public class DefaultSheetStyleBuilder : ISheetStyleBuilder +namespace MiniExcelLibs.OpenXml.Styles +{ + internal class DefaultSheetStyleBuilder : SheetStyleBuilderBase { - 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 - ( $@" - - - - - {NumFmtsToken} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {cellXfsToken} - - - - - " - ); - - public string Build( ICollection columns ) + internal static SheetStyleElementInfos GenerateElementInfos = new SheetStyleElementInfos + { + NumFmtCount = 0,//默认的NumFmt数量是0,但是会有根据ColumnsToApply动态生成的NumFmt + FontCount = 2, + FillCount = 3, + BorderCount = 2, + CellStyleXfCount = 3, + CellXfCount = 5 + }; + + private readonly SheetStyleBuildContext _context; + + public DefaultSheetStyleBuilder(SheetStyleBuildContext context) : base(context) + { + _context = context; + } + + protected override SheetStyleElementInfos GetGenerateElementInfos() + { + return GenerateElementInfos; + } + + protected override void GenerateNumFmt() { const int numFmtIndex = 166; - var sb = new StringBuilder( DefaultStylesXml ); - var columnsToApply = SheetStyleBuilderHelper.GenerateStyleIds( startUpCellXfs, columns ); + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; + + /* + * + protected override async Task GenerateNumFmtAsync() + { + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) { - return new - { - numFmt = $@"", - - cellXfs = $@" - - " - }; - } ).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(); + index++; + + /* + * + * + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "vertAlign", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "baseline"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "sz", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "11"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "name", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "Calibri"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "family", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "2"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "vertAlign", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "baseline"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "sz", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "11"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FFFFFFFF"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "name", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "Calibri"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "family", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("val", "2"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + + protected override async Task GenerateFontAsync() + { + /* + * + * + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "vertAlign", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "baseline"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "sz", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "11"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "name", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "Calibri"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "family", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "2"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "vertAlign", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "baseline"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "sz", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "11"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FFFFFFFF"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "name", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "Calibri"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "family", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "val", null, "2"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + + protected override void GenerateFill() + { + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("patternType", "none"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("patternType", "gray125"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("patternType", "solid"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fgColor", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "284472C4"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + + protected override async Task GenerateFillAsync() + { + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "patternType", null, "none"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "patternType", null, "gray125"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "patternFill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "patternType", null, "solid"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fgColor", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "284472C4"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + + protected override void GenerateBorder() + { + /* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("diagonalUp", "0"); + _context.NewXmlWriter.WriteAttributeString("diagonalDown", "0"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "left", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "right", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "top", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "bottom", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "diagonal", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("diagonalUp", "0"); + _context.NewXmlWriter.WriteAttributeString("diagonalDown", "0"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "left", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "thin"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "right", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "thin"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "top", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "thin"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "bottom", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "thin"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "diagonal", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("style", "none"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("rgb", "FF000000"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + + protected override async Task GenerateBorderAsync() + { + /* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "diagonalUp", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "diagonalDown", null, "0"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "left", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "right", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "top", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "bottom", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "diagonal", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "diagonalUp", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "diagonalDown", null, "0"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "left", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "thin"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "right", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "thin"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "top", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "thin"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "bottom", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "thin"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "diagonal", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "style", null, "none"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "color", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, "FF000000"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + + protected override void GenerateCellStyleXf() + { + /* + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "0"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "1"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "0"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "14"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 2}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "0"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "0"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "1"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + + protected override async Task GenerateCellStyleXfAsync() + { + /* + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "14"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 2}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + + protected override void GenerateCellXf() + { + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "0"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 2}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("xfId", "0"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "0"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("horizontal", "left"); + _context.NewXmlWriter.WriteAttributeString("vertical", "bottom"); + _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); + _context.NewXmlWriter.WriteAttributeString("wrapText", "0"); + _context.NewXmlWriter.WriteAttributeString("indent", "0"); + _context.NewXmlWriter.WriteAttributeString("relativeIndent", "0"); + _context.NewXmlWriter.WriteAttributeString("justifyLastLine", "0"); + _context.NewXmlWriter.WriteAttributeString("shrinkToFit", "0"); + _context.NewXmlWriter.WriteAttributeString("readingOrder", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "0"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("xfId", "0"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "1"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("horizontal", "general"); + _context.NewXmlWriter.WriteAttributeString("vertical", "bottom"); + _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); + _context.NewXmlWriter.WriteAttributeString("wrapText", "0"); + _context.NewXmlWriter.WriteAttributeString("indent", "0"); + _context.NewXmlWriter.WriteAttributeString("relativeIndent", "0"); + _context.NewXmlWriter.WriteAttributeString("justifyLastLine", "0"); + _context.NewXmlWriter.WriteAttributeString("shrinkToFit", "0"); + _context.NewXmlWriter.WriteAttributeString("readingOrder", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "14"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("xfId", "0"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "1"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("horizontal", "general"); + _context.NewXmlWriter.WriteAttributeString("vertical", "bottom"); + _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); + _context.NewXmlWriter.WriteAttributeString("wrapText", "0"); + _context.NewXmlWriter.WriteAttributeString("indent", "0"); + _context.NewXmlWriter.WriteAttributeString("relativeIndent", "0"); + _context.NewXmlWriter.WriteAttributeString("justifyLastLine", "0"); + _context.NewXmlWriter.WriteAttributeString("shrinkToFit", "0"); + _context.NewXmlWriter.WriteAttributeString("readingOrder", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "0"); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("xfId", "0"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("horizontal", "fill"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; + + /* + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", (numFmtIndex + index + _context.OldElementInfos.NumFmtCount).ToString()); + _context.NewXmlWriter.WriteAttributeString("fontId", $"{_context.OldElementInfos.FontCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("fillId", $"{_context.OldElementInfos.FillCount + 0}"); + _context.NewXmlWriter.WriteAttributeString("borderId", $"{_context.OldElementInfos.BorderCount + 1}"); + _context.NewXmlWriter.WriteAttributeString("xfId", "0"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteAttributeString("applyFill", "1"); + _context.NewXmlWriter.WriteAttributeString("applyBorder", "1"); + _context.NewXmlWriter.WriteAttributeString("applyAlignment", "1"); + _context.NewXmlWriter.WriteAttributeString("applyProtection", "1"); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("horizontal", "general"); + _context.NewXmlWriter.WriteAttributeString("vertical", "bottom"); + _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); + _context.NewXmlWriter.WriteAttributeString("wrapText", "0"); + _context.NewXmlWriter.WriteAttributeString("indent", "0"); + _context.NewXmlWriter.WriteAttributeString("relativeIndent", "0"); + _context.NewXmlWriter.WriteAttributeString("justifyLastLine", "0"); + _context.NewXmlWriter.WriteAttributeString("shrinkToFit", "0"); + _context.NewXmlWriter.WriteAttributeString("readingOrder", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("locked", "1"); + _context.NewXmlWriter.WriteAttributeString("hidden", "0"); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + } + + protected override async Task GenerateCellXfAsync() + { + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 2}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "xfId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "horizontal", null, "left"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "vertical", null, "bottom"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "textRotation", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "wrapText", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "indent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "relativeIndent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "justifyLastLine", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "shrinkToFit", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "readingOrder", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "xfId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "horizontal", null, "general"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "vertical", null, "bottom"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "textRotation", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "wrapText", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "indent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "relativeIndent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "justifyLastLine", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "shrinkToFit", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "readingOrder", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "14"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "xfId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "horizontal", null, "general"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "vertical", null, "bottom"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "textRotation", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "wrapText", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "indent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "relativeIndent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "justifyLastLine", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "shrinkToFit", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "readingOrder", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "xfId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "horizontal", null, "fill"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; + + /* + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, (numFmtIndex + index + _context.OldElementInfos.NumFmtCount).ToString()); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fontId", null, $"{_context.OldElementInfos.FontCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "fillId", null, $"{_context.OldElementInfos.FillCount + 0}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "borderId", null, $"{_context.OldElementInfos.BorderCount + 1}"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "xfId", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyFill", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyBorder", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyAlignment", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyProtection", null, "1"); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "alignment", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "horizontal", null, "general"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "vertical", null, "bottom"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "textRotation", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "wrapText", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "indent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "relativeIndent", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "justifyLastLine", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "shrinkToFit", null, "0"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "readingOrder", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "protection", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "locked", null, "1"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "hidden", null, "0"); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + } + } } diff --git a/src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs b/src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs index 5be8dca0..239fdde8 100644 --- a/src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs +++ b/src/MiniExcel/OpenXml/Styles/ISheetStyleBuilder.cs @@ -1,10 +1,12 @@ -using MiniExcelLibs.Attributes; -using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace MiniExcelLibs.OpenXml.Styles { - public interface ISheetStyleBuilder +namespace MiniExcelLibs.OpenXml.Styles +{ + internal interface ISheetStyleBuilder { - string Build( ICollection columns ); - } + SheetStyleBuildResult Build(); + Task BuildAsync(CancellationToken cancellationToken = default); + } } diff --git a/src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs b/src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs index ec7e87f5..e3af418e 100644 --- a/src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs +++ b/src/MiniExcel/OpenXml/Styles/MinimalSheetStyleBuilder.cs @@ -1,71 +1,214 @@ -using MiniExcelLibs.Attributes; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Threading.Tasks; -namespace MiniExcelLibs.OpenXml.Styles { - public class MinimalSheetStyleBuilder : ISheetStyleBuilder +namespace MiniExcelLibs.OpenXml.Styles +{ + internal class MinimalSheetStyleBuilder : SheetStyleBuilderBase { - 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 - ( $@" - - - - - {NumFmtsToken} - - - - - - - - - - - - - - - - - - - - {cellXfsToken} - - " - ); - - public string Build( ICollection columns ) + internal static SheetStyleElementInfos GenerateElementInfos = new SheetStyleElementInfos + { + NumFmtCount = 0,//默认的NumFmt数量是0,但是会有根据ColumnsToApply动态生成的NumFmt + FontCount = 1, + FillCount = 1, + BorderCount = 1, + CellStyleXfCount = 1, + CellXfCount = 5 + }; + + private readonly SheetStyleBuildContext _context; + + public MinimalSheetStyleBuilder(SheetStyleBuildContext context) : base(context) + { + _context = context; + } + + protected override SheetStyleElementInfos GetGenerateElementInfos() + { + return GenerateElementInfos; + } + + protected override void GenerateNumFmt() { const int numFmtIndex = 166; - var sb = new StringBuilder( NoneStylesXml ); - var columnsToApply = SheetStyleBuilderHelper.GenerateStyleIds( startUpCellXfs, columns ); + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; - var numFmts = columnsToApply.Select( ( x, i ) => { - return new { - numFmt = $@"", - cellXfs = $@"" - }; - } ).ToArray(); + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "numFmt", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", (numFmtIndex + index + _context.OldElementInfos.NumFmtCount).ToString()); + _context.NewXmlWriter.WriteAttributeString("formatCode", item.Format); + _context.NewXmlWriter.WriteFullEndElement(); + } + } - sb.Replace( NumFmtsToken, string.Join( string.Empty, numFmts.Select( x => x.numFmt ) ) ); - sb.Replace( NumFmtsCountToken, (startUpNumFmts + numFmts.Length).ToString() ); + protected override async Task GenerateNumFmtAsync() + { + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; - sb.Replace( cellXfsToken, string.Join( string.Empty, numFmts.Select( x => x.cellXfs ) ) ); - sb.Replace( cellXfsCountToken, (5 + numFmts.Length).ToString() ); - return sb.ToString(); + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "numFmt", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, (numFmtIndex + index + _context.OldElementInfos.NumFmtCount).ToString()); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "formatCode", null, item.Format); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + } + } + + protected override void GenerateFont() + { + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + } + + protected override async Task GenerateFontAsync() + { + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "font", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + } + + protected override void GenerateFill() + { + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + } + + protected override async Task GenerateFillAsync() + { + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fill", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); } - } + protected override void GenerateBorder() + { + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + } + + protected override async Task GenerateBorderAsync() + { + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "border", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + } + + protected override void GenerateCellStyleXf() + { + /* + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + } + + protected override async Task GenerateCellStyleXfAsync() + { + /* + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + } + + protected override void GenerateCellXf() + { + /* + * + * + * + * + * + */ + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("numFmtId", "14"); + _context.NewXmlWriter.WriteAttributeString("applyNumberFormat", "1"); + _context.NewXmlWriter.WriteFullEndElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteFullEndElement(); + + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; + + /* + * + * + * + * + * + */ + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "numFmtId", null, "14"); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "applyNumberFormat", null, "1"); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "xf", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + const int numFmtIndex = 166; + var index = 0; + foreach (var item in _context.ColumnsToApply) + { + index++; + + /* + * + + " + ); + + private readonly Dictionary _zipDictionary; + private readonly MiniExcelZipArchive _archive; + private readonly Encoding _encoding; + private readonly ICollection _columns; + + private StringReader emptyStylesXmlStringReader; + private ZipArchiveEntry oldStyleXmlZipEntry; + private ZipArchiveEntry newStyleXmlZipEntry; + private Stream oldXmlReaderStream; + private Stream newXmlWriterStream; + private bool initialized = false; + private bool finalized = false; + private bool disposed = false; + + public SheetStyleBuildContext(Dictionary zipDictionary, MiniExcelZipArchive archive, Encoding encoding, ICollection columns) + { + _zipDictionary = zipDictionary; + _archive = archive; + _encoding = encoding; + _columns = columns; + } + + public XmlReader OldXmlReader { get; private set; } + + public XmlWriter NewXmlWriter { get; private set; } + + public SheetStyleElementInfos OldElementInfos { get; private set; } + + public SheetStyleElementInfos GenerateElementInfos { get; private set; } + + public IEnumerable ColumnsToApply { get; private set; } + + public int CustomFormatCount { get; private set; } + + public void Initialize(SheetStyleElementInfos generateElementInfos) + { + if (initialized) + { + throw new InvalidOperationException("The context has been initialized."); + } + + oldStyleXmlZipEntry = _archive.Mode == ZipArchiveMode.Update ? _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Styles) : null; + if (oldStyleXmlZipEntry != null) + { + using (var oldStyleXmlStream = oldStyleXmlZipEntry.Open()) + { + OldElementInfos = ReadSheetStyleElementInfos(XmlReader.Create(oldStyleXmlStream, new XmlReaderSettings() { IgnoreWhitespace = true })); + } + + oldXmlReaderStream = oldStyleXmlZipEntry.Open(); + OldXmlReader = XmlReader.Create(oldXmlReaderStream, new XmlReaderSettings() { IgnoreWhitespace = true }); + + newStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles + ".temp", CompressionLevel.Fastest); + } + else + { + OldElementInfos = new SheetStyleElementInfos(); + + emptyStylesXmlStringReader = new StringReader(_emptyStylesXml); + OldXmlReader = XmlReader.Create(emptyStylesXmlStringReader, new XmlReaderSettings() { IgnoreWhitespace = true }); + + newStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest); + } + + newXmlWriterStream = newStyleXmlZipEntry.Open(); + NewXmlWriter = XmlWriter.Create(newXmlWriterStream, new XmlWriterSettings() { Indent = true, Encoding = _encoding }); + + GenerateElementInfos = generateElementInfos; + ColumnsToApply = SheetStyleBuilderHelper.GenerateStyleIds(OldElementInfos.CellXfCount + generateElementInfos.CellXfCount, _columns).ToArray();//这里暂时加ToArray,避免多次计算,如果有性能问题再考虑优化 + CustomFormatCount = ColumnsToApply.Count(); + + initialized = true; + } + + public async Task InitializeAsync(SheetStyleElementInfos generateElementInfos) + { + if (initialized) + { + throw new InvalidOperationException("The context has been initialized."); + } + + oldStyleXmlZipEntry = _archive.Mode == ZipArchiveMode.Update ? _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Styles) : null; + if (oldStyleXmlZipEntry != null) + { + using (var oldStyleXmlStream = oldStyleXmlZipEntry.Open()) + { + OldElementInfos = await ReadSheetStyleElementInfosAsync(XmlReader.Create(oldStyleXmlStream, new XmlReaderSettings() { IgnoreWhitespace = true, Async = true })); + } + oldXmlReaderStream = oldStyleXmlZipEntry.Open(); + OldXmlReader = XmlReader.Create(oldXmlReaderStream, new XmlReaderSettings() { IgnoreWhitespace = true, Async = true }); + + newStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles + ".temp", CompressionLevel.Fastest); + } + else + { + OldElementInfos = new SheetStyleElementInfos(); + emptyStylesXmlStringReader = new StringReader(_emptyStylesXml); + OldXmlReader = XmlReader.Create(emptyStylesXmlStringReader, new XmlReaderSettings() { IgnoreWhitespace = true, Async = true }); + + newStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest); + } + + newXmlWriterStream = newStyleXmlZipEntry.Open(); + NewXmlWriter = XmlWriter.Create(newXmlWriterStream, new XmlWriterSettings() { Indent = true, Encoding = _encoding, Async = true }); + + GenerateElementInfos = generateElementInfos; + ColumnsToApply = SheetStyleBuilderHelper.GenerateStyleIds(OldElementInfos.CellXfCount + generateElementInfos.CellXfCount, _columns); + CustomFormatCount = ColumnsToApply.Count(); + + initialized = true; + } + + public void FinalizeAndUpdateZipDictionary() + { + if (!initialized) + { + throw new InvalidOperationException("The context has not been initialized."); + } + if (disposed) + { + throw new ObjectDisposedException(nameof(SheetStyleBuildContext)); + } + if (finalized) + { + throw new InvalidOperationException("The context has been finalized."); + } + try + { + OldXmlReader.Dispose(); + OldXmlReader = null; + oldXmlReaderStream?.Dispose(); + oldXmlReaderStream = null; + emptyStylesXmlStringReader?.Dispose(); + emptyStylesXmlStringReader = null; + + NewXmlWriter.Flush(); + NewXmlWriter.Close(); + NewXmlWriter.Dispose(); + NewXmlWriter = null; + newXmlWriterStream.Dispose(); + newXmlWriterStream = null; + + if (oldStyleXmlZipEntry == null) + { + _zipDictionary.Add(ExcelFileNames.Styles, new ZipPackageInfo(newStyleXmlZipEntry, ExcelContentTypes.Styles)); + } + else + { + oldStyleXmlZipEntry?.Delete(); + oldStyleXmlZipEntry = null; + var finalStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest); + using (var tempStream = newStyleXmlZipEntry.Open()) + using (var newStream = finalStyleXmlZipEntry.Open()) + { + tempStream.CopyTo(newStream); + } + _zipDictionary[ExcelFileNames.Styles] = new ZipPackageInfo(finalStyleXmlZipEntry, ExcelContentTypes.Styles); + newStyleXmlZipEntry.Delete(); + newStyleXmlZipEntry = null; + } + + finalized = true; + } + catch (Exception ex) + { + throw new Exception("Failed to finalize and replace styles.", ex); + } + } + + public async Task FinalizeAndUpdateZipDictionaryAsync() + { + if (!initialized) + { + throw new InvalidOperationException("The context has not been initialized."); + } + if (disposed) + { + throw new ObjectDisposedException(nameof(SheetStyleBuildContext)); + } + if (finalized) + { + throw new InvalidOperationException("The context has been finalized."); + } + try + { + OldXmlReader.Dispose(); + OldXmlReader = null; + oldXmlReaderStream?.Dispose(); + oldXmlReaderStream = null; + emptyStylesXmlStringReader?.Dispose(); + emptyStylesXmlStringReader = null; + + await NewXmlWriter.FlushAsync(); + NewXmlWriter.Close(); + NewXmlWriter.Dispose(); + NewXmlWriter = null; + newXmlWriterStream.Dispose(); + newXmlWriterStream = null; + + if (oldStyleXmlZipEntry == null) + { + _zipDictionary.Add(ExcelFileNames.Styles, new ZipPackageInfo(newStyleXmlZipEntry, ExcelContentTypes.Styles)); + } + else + { + oldStyleXmlZipEntry?.Delete(); + oldStyleXmlZipEntry = null; + var finalStyleXmlZipEntry = _archive.CreateEntry(ExcelFileNames.Styles, CompressionLevel.Fastest); + using (var tempStream = newStyleXmlZipEntry.Open()) + using (var newStream = finalStyleXmlZipEntry.Open()) + { + await tempStream.CopyToAsync(newStream); + } + _zipDictionary[ExcelFileNames.Styles] = new ZipPackageInfo(finalStyleXmlZipEntry, ExcelContentTypes.Styles); + newStyleXmlZipEntry.Delete(); + newStyleXmlZipEntry = null; + } + + finalized = true; + } + catch (Exception ex) + { + throw new Exception("Failed to finalize and replace styles.", ex); + } + } + + private static SheetStyleElementInfos ReadSheetStyleElementInfos(XmlReader reader) + { + var elementInfos = new SheetStyleElementInfos(); + while (reader.Read()) + { + SetElementInfos(reader, elementInfos); + } + return elementInfos; + } + + private static async Task ReadSheetStyleElementInfosAsync(XmlReader reader) + { + var elementInfos = new SheetStyleElementInfos(); + while (await reader.ReadAsync()) + { + SetElementInfos(reader, elementInfos); + } + return elementInfos; + } + + private static void SetElementInfos(XmlReader reader, SheetStyleElementInfos elementInfos) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.LocalName) + { + case "numFmts": + elementInfos.ExistsNumFmts = true; + elementInfos.NumFmtCount = GetCount(); + break; + case "fonts": + elementInfos.ExistsFonts = true; + elementInfos.FontCount = GetCount(); + break; + case "fills": + elementInfos.ExistsFills = true; + elementInfos.FillCount = GetCount(); + break; + case "borders": + elementInfos.ExistsBorders = true; + elementInfos.BorderCount = GetCount(); + break; + case "cellStyleXfs": + elementInfos.ExistsCellStyleXfs = true; + elementInfos.CellStyleXfCount = GetCount(); + break; + case "cellXfs": + elementInfos.ExistsCellXfs = true; + elementInfos.CellXfCount = GetCount(); + break; + } + + int GetCount() + { + string count = reader.GetAttribute("count"); + if (!string.IsNullOrEmpty(count) && int.TryParse(count, out int countValue)) + { + return countValue; + } + return 0; + } + } + } + + public void Dispose() + { + Dispose(true); + + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposed) + { + if (disposing) + { + OldXmlReader?.Dispose(); + oldXmlReaderStream?.Dispose(); + emptyStylesXmlStringReader?.Dispose(); + + NewXmlWriter?.Dispose(); + newXmlWriterStream?.Dispose(); + } + + disposed = true; + } + } + } +} diff --git a/src/MiniExcel/OpenXml/Styles/SheetStyleBuildResult.cs b/src/MiniExcel/OpenXml/Styles/SheetStyleBuildResult.cs new file mode 100644 index 00000000..8c3e3878 --- /dev/null +++ b/src/MiniExcel/OpenXml/Styles/SheetStyleBuildResult.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace MiniExcelLibs.OpenXml.Styles +{ + internal class SheetStyleBuildResult + { + public SheetStyleBuildResult(Dictionary cellXfIdMap) + { + CellXfIdMap = cellXfIdMap; + } + + public Dictionary CellXfIdMap { get; set; } + } +} diff --git a/src/MiniExcel/OpenXml/Styles/SheetStyleBuilderBase.cs b/src/MiniExcel/OpenXml/Styles/SheetStyleBuilderBase.cs new file mode 100644 index 00000000..f056cec3 --- /dev/null +++ b/src/MiniExcel/OpenXml/Styles/SheetStyleBuilderBase.cs @@ -0,0 +1,586 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MiniExcelLibs.OpenXml.Styles +{ + internal abstract class SheetStyleBuilderBase : ISheetStyleBuilder + { + internal readonly static Dictionary _allElements = new Dictionary + { + ["numFmts"] = 0, + ["fonts"] = 1, + ["fills"] = 2, + ["borders"] = 3, + ["cellStyleXfs"] = 4, + ["cellXfs"] = 5, + ["cellStyles"] = 6, + ["dxfs"] = 7, + ["tableStyles"] = 8, + ["extLst"] = 9 + }; + + private readonly SheetStyleBuildContext _context; + + public SheetStyleBuilderBase(SheetStyleBuildContext context) + { + _context = context; + } + + public virtual SheetStyleBuildResult Build() + { + _context.Initialize(GetGenerateElementInfos()); + + while (_context.OldXmlReader.Read()) + { + switch (_context.OldXmlReader.NodeType) + { + case XmlNodeType.Element: + GenerateElementBeforStartElement(); + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, _context.OldXmlReader.LocalName, _context.OldXmlReader.NamespaceURI); + WriteAttributes(_context.OldXmlReader.LocalName); + if (_context.OldXmlReader.IsEmptyElement) + { + GenerateElementBeforEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } + break; + + case XmlNodeType.Text: + _context.NewXmlWriter.WriteString(_context.OldXmlReader.Value); + break; + + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + _context.NewXmlWriter.WriteWhitespace(_context.OldXmlReader.Value); + break; + + case XmlNodeType.CDATA: + _context.NewXmlWriter.WriteCData(_context.OldXmlReader.Value); + break; + + case XmlNodeType.EntityReference: + _context.NewXmlWriter.WriteEntityRef(_context.OldXmlReader.Name); + break; + + case XmlNodeType.XmlDeclaration: + case XmlNodeType.ProcessingInstruction: + _context.NewXmlWriter.WriteProcessingInstruction(_context.OldXmlReader.Name, _context.OldXmlReader.Value); + break; + case XmlNodeType.DocumentType: + _context.NewXmlWriter.WriteDocType(_context.OldXmlReader.Name, _context.OldXmlReader.GetAttribute("PUBLIC"), _context.OldXmlReader.GetAttribute("SYSTEM"), _context.OldXmlReader.Value); + break; + + case XmlNodeType.Comment: + _context.NewXmlWriter.WriteComment(_context.OldXmlReader.Value); + break; + case XmlNodeType.EndElement: + GenerateElementBeforEndElement(); + _context.NewXmlWriter.WriteFullEndElement(); + break; + } + } + + _context.FinalizeAndUpdateZipDictionary(); + + return new SheetStyleBuildResult(GetCellXfIdMap()); + } + + public virtual async Task BuildAsync(CancellationToken cancellationToken = default) + { + await _context.InitializeAsync(GetGenerateElementInfos()); + + while (await _context.OldXmlReader.ReadAsync()) + { + cancellationToken.ThrowIfCancellationRequested(); + + switch (_context.OldXmlReader.NodeType) + { + case XmlNodeType.Element: + await GenerateElementBeforStartElementAsync(); + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, _context.OldXmlReader.LocalName, _context.OldXmlReader.NamespaceURI); + await WriteAttributesAsync(_context.OldXmlReader.LocalName); + if (_context.OldXmlReader.IsEmptyElement) + { + await GenerateElementBeforEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } + break; + case XmlNodeType.Text: + await _context.NewXmlWriter.WriteStringAsync(_context.OldXmlReader.Value); + break; + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + await _context.NewXmlWriter.WriteWhitespaceAsync(_context.OldXmlReader.Value); + break; + case XmlNodeType.CDATA: + await _context.NewXmlWriter.WriteCDataAsync(_context.OldXmlReader.Value); + break; + case XmlNodeType.EntityReference: + await _context.NewXmlWriter.WriteEntityRefAsync(_context.OldXmlReader.Name); + break; + case XmlNodeType.XmlDeclaration: + case XmlNodeType.ProcessingInstruction: + await _context.NewXmlWriter.WriteProcessingInstructionAsync(_context.OldXmlReader.Name, _context.OldXmlReader.Value); + break; + case XmlNodeType.DocumentType: + await _context.NewXmlWriter.WriteDocTypeAsync(_context.OldXmlReader.Name, _context.OldXmlReader.GetAttribute("PUBLIC"), _context.OldXmlReader.GetAttribute("SYSTEM"), _context.OldXmlReader.Value); + break; + case XmlNodeType.Comment: + await _context.NewXmlWriter.WriteCommentAsync(_context.OldXmlReader.Value); + break; + case XmlNodeType.EndElement: + await GenerateElementBeforEndElementAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + break; + } + } + + await _context.FinalizeAndUpdateZipDictionaryAsync(); + + return new SheetStyleBuildResult(GetCellXfIdMap()); + } + + protected abstract SheetStyleElementInfos GetGenerateElementInfos(); + + protected virtual void WriteAttributes(string element) + { + if (_context.OldXmlReader.NodeType is XmlNodeType.Element || _context.OldXmlReader.NodeType is XmlNodeType.XmlDeclaration) + { + if (_context.OldXmlReader.MoveToFirstAttribute()) + { + WriteAttributes(element); + _context.OldXmlReader.MoveToElement(); + } + } + else if (_context.OldXmlReader.NodeType == XmlNodeType.Attribute) + { + do + { + _context.NewXmlWriter.WriteStartAttribute(_context.OldXmlReader.Prefix, _context.OldXmlReader.LocalName, _context.OldXmlReader.NamespaceURI); + var currentAttribute = _context.OldXmlReader.LocalName; + while (_context.OldXmlReader.ReadAttributeValue()) + { + if (_context.OldXmlReader.NodeType == XmlNodeType.EntityReference) + { + _context.NewXmlWriter.WriteEntityRef(_context.OldXmlReader.Name); + } + else if (currentAttribute == "count") + { + switch (element) + { + case "numFmts": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.NumFmtCount + _context.GenerateElementInfos.NumFmtCount + _context.CustomFormatCount).ToString()); + break; + case "fonts": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.FontCount + _context.GenerateElementInfos.FontCount).ToString()); + break; + case "fills": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.FillCount + _context.GenerateElementInfos.FillCount).ToString()); + break; + case "borders": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.BorderCount + _context.GenerateElementInfos.BorderCount).ToString()); + break; + case "cellStyleXfs": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.CellStyleXfCount + _context.GenerateElementInfos.CellStyleXfCount).ToString()); + break; + case "cellXfs": + _context.NewXmlWriter.WriteString((_context.OldElementInfos.CellXfCount + _context.GenerateElementInfos.CellXfCount + _context.CustomFormatCount).ToString()); + break; + default: + _context.NewXmlWriter.WriteString(_context.OldXmlReader.Value); + break; + } + } + else + { + _context.NewXmlWriter.WriteString(_context.OldXmlReader.Value); + } + } + _context.NewXmlWriter.WriteEndAttribute(); + } + while (_context.OldXmlReader.MoveToNextAttribute()); + } + } + + protected virtual async Task WriteAttributesAsync(string element) + { + if (_context.OldXmlReader.NodeType is XmlNodeType.Element || _context.OldXmlReader.NodeType is XmlNodeType.XmlDeclaration) + { + if (_context.OldXmlReader.MoveToFirstAttribute()) + { + await WriteAttributesAsync(element); + _context.OldXmlReader.MoveToElement(); + } + } + else if (_context.OldXmlReader.NodeType == XmlNodeType.Attribute) + { + do + { + _context.NewXmlWriter.WriteStartAttribute(_context.OldXmlReader.Prefix, _context.OldXmlReader.LocalName, _context.OldXmlReader.NamespaceURI); + var currentAttribute = _context.OldXmlReader.LocalName; + while (_context.OldXmlReader.ReadAttributeValue()) + { + if (_context.OldXmlReader.NodeType == XmlNodeType.EntityReference) + { + await _context.NewXmlWriter.WriteEntityRefAsync(_context.OldXmlReader.Name); + } + else if (currentAttribute == "count") + { + switch (element) + { + case "numFmts": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.NumFmtCount + _context.GenerateElementInfos.NumFmtCount + _context.CustomFormatCount).ToString()); + break; + case "fonts": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.FontCount + _context.GenerateElementInfos.FontCount).ToString()); + break; + case "fills": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.FillCount + _context.GenerateElementInfos.FillCount).ToString()); + break; + case "borders": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.BorderCount + _context.GenerateElementInfos.BorderCount).ToString()); + break; + case "cellStyleXfs": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.CellStyleXfCount + _context.GenerateElementInfos.CellStyleXfCount).ToString()); + break; + case "cellXfs": + await _context.NewXmlWriter.WriteStringAsync((_context.OldElementInfos.CellXfCount + _context.GenerateElementInfos.CellXfCount + _context.CustomFormatCount).ToString()); + break; + default: + await _context.NewXmlWriter.WriteStringAsync(_context.OldXmlReader.Value); + break; + } + } + else + { + await _context.NewXmlWriter.WriteStringAsync(_context.OldXmlReader.Value); + } + } + _context.NewXmlWriter.WriteEndAttribute(); + } + while (_context.OldXmlReader.MoveToNextAttribute()); + } + } + + protected virtual void GenerateElementBeforStartElement() + { + if (!_allElements.TryGetValue(_context.OldXmlReader.LocalName, out var elementIndex)) + { + return; + } + if (!_context.OldElementInfos.ExistsNumFmts && !_context.GenerateElementInfos.ExistsNumFmts && _allElements["numFmts"] < elementIndex) + { + GenerateNumFmts(); + _context.GenerateElementInfos.ExistsNumFmts = true; + } + else if (!_context.OldElementInfos.ExistsFonts && !_context.GenerateElementInfos.ExistsFonts && _allElements["fonts"] < elementIndex) + { + GenerateFonts(); + _context.GenerateElementInfos.ExistsFonts = true; + } + else if (!_context.OldElementInfos.ExistsFills && !_context.GenerateElementInfos.ExistsFills && _allElements["fills"] < elementIndex) + { + GenerateFills(); + _context.GenerateElementInfos.ExistsFills = true; + } + else if (!_context.OldElementInfos.ExistsBorders && !_context.GenerateElementInfos.ExistsBorders && _allElements["borders"] < elementIndex) + { + GenerateBorders(); + _context.GenerateElementInfos.ExistsBorders = true; + } + else if (!_context.OldElementInfos.ExistsCellStyleXfs && !_context.GenerateElementInfos.ExistsCellStyleXfs && _allElements["cellStyleXfs"] < elementIndex) + { + GenerateCellStyleXfs(); + _context.GenerateElementInfos.ExistsCellStyleXfs = true; + } + else if (!_context.OldElementInfos.ExistsCellXfs && !_context.GenerateElementInfos.ExistsCellXfs && _allElements["cellXfs"] < elementIndex) + { + GenerateCellXfs(); + _context.GenerateElementInfos.ExistsCellXfs = true; + } + } + + protected virtual async Task GenerateElementBeforStartElementAsync() + { + if (!_allElements.TryGetValue(_context.OldXmlReader.LocalName, out var elementIndex)) + { + return; + } + if (!_context.OldElementInfos.ExistsNumFmts && !_context.GenerateElementInfos.ExistsNumFmts && _allElements["numFmts"] < elementIndex) + { + await GenerateNumFmtsAsync(); + _context.GenerateElementInfos.ExistsNumFmts = true; + } + else if (!_context.OldElementInfos.ExistsFonts && !_context.GenerateElementInfos.ExistsFonts && _allElements["fonts"] < elementIndex) + { + await GenerateFontsAsync(); + _context.GenerateElementInfos.ExistsFonts = true; + } + else if (!_context.OldElementInfos.ExistsFills && !_context.GenerateElementInfos.ExistsFills && _allElements["fills"] < elementIndex) + { + await GenerateFillsAsync(); + _context.GenerateElementInfos.ExistsFills = true; + } + else if (!_context.OldElementInfos.ExistsBorders && !_context.GenerateElementInfos.ExistsBorders && _allElements["borders"] < elementIndex) + { + await GenerateBordersAsync(); + _context.GenerateElementInfos.ExistsBorders = true; + } + else if (!_context.OldElementInfos.ExistsCellStyleXfs && !_context.GenerateElementInfos.ExistsCellStyleXfs && _allElements["cellStyleXfs"] < elementIndex) + { + await GenerateCellStyleXfsAsync(); + _context.GenerateElementInfos.ExistsCellStyleXfs = true; + } + else if (!_context.OldElementInfos.ExistsCellXfs && !_context.GenerateElementInfos.ExistsCellXfs && _allElements["cellXfs"] < elementIndex) + { + await GenerateCellXfsAsync(); + _context.GenerateElementInfos.ExistsCellXfs = true; + } + } + + protected virtual void GenerateElementBeforEndElement() + { + if (_context.OldXmlReader.LocalName == "styleSheet" && !_context.OldElementInfos.ExistsNumFmts && !_context.GenerateElementInfos.ExistsNumFmts) + { + GenerateNumFmts(); + } + else if (_context.OldXmlReader.LocalName == "numFmts") + { + GenerateNumFmt(); + } + else if (_context.OldXmlReader.LocalName == "fonts") + { + GenerateFont(); + } + else if (_context.OldXmlReader.LocalName == "fills") + { + GenerateFill(); + } + else if (_context.OldXmlReader.LocalName == "borders") + { + GenerateBorder(); + } + else if (_context.OldXmlReader.LocalName == "cellStyleXfs") + { + GenerateCellStyleXf(); + } + else if (_context.OldXmlReader.LocalName == "cellXfs") + { + GenerateCellXf(); + } + } + + protected virtual async Task GenerateElementBeforEndElementAsync() + { + if (_context.OldXmlReader.LocalName == "styleSheet" && !_context.OldElementInfos.ExistsNumFmts && !_context.GenerateElementInfos.ExistsNumFmts) + { + await GenerateNumFmtsAsync(); + } + else if (_context.OldXmlReader.LocalName == "numFmts") + { + await GenerateNumFmtAsync(); + } + else if (_context.OldXmlReader.LocalName == "fonts") + { + await GenerateFontAsync(); + } + else if (_context.OldXmlReader.LocalName == "fills") + { + await GenerateFillAsync(); + } + else if (_context.OldXmlReader.LocalName == "borders") + { + await GenerateBorderAsync(); + } + else if (_context.OldXmlReader.LocalName == "cellStyleXfs") + { + await GenerateCellStyleXfAsync(); + } + else if (_context.OldXmlReader.LocalName == "cellXfs") + { + await GenerateCellXfAsync(); + } + } + + protected virtual void GenerateNumFmts() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "numFmts", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.NumFmtCount + _context.GenerateElementInfos.NumFmtCount + _context.CustomFormatCount).ToString()); + GenerateNumFmt(); + _context.NewXmlWriter.WriteFullEndElement(); + + if (!_context.OldElementInfos.ExistsFonts) + { + GenerateFonts(); + } + } + + protected virtual async Task GenerateNumFmtsAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "numFmts", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.NumFmtCount + _context.GenerateElementInfos.NumFmtCount + _context.CustomFormatCount).ToString()); + await GenerateNumFmtAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + if (!_context.OldElementInfos.ExistsFonts) + { + await GenerateFontsAsync(); + } + } + + protected abstract void GenerateNumFmt(); + + protected abstract Task GenerateNumFmtAsync(); + + protected virtual void GenerateFonts() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fonts", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.FontCount + _context.GenerateElementInfos.FontCount).ToString()); + GenerateFont(); + _context.NewXmlWriter.WriteFullEndElement(); + + if (!_context.OldElementInfos.ExistsFills) + { + GenerateFills(); + } + } + + protected virtual async Task GenerateFontsAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fonts", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.FontCount + _context.GenerateElementInfos.FontCount).ToString()); + await GenerateFontAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + if (!_context.OldElementInfos.ExistsFills) + { + await GenerateFillsAsync(); + } + } + + protected abstract void GenerateFont(); + + protected abstract Task GenerateFontAsync(); + + protected virtual void GenerateFills() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "fills", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.FillCount + _context.GenerateElementInfos.FillCount).ToString()); + GenerateFill(); + _context.NewXmlWriter.WriteFullEndElement(); + if (!_context.OldElementInfos.ExistsBorders) + { + GenerateBorders(); + } + } + + protected virtual async Task GenerateFillsAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "fills", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.FillCount + _context.GenerateElementInfos.FillCount).ToString()); + await GenerateFillAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + if (!_context.OldElementInfos.ExistsBorders) + { + await GenerateBordersAsync(); + } + } + + protected abstract void GenerateFill(); + + protected abstract Task GenerateFillAsync(); + + protected virtual void GenerateBorders() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "borders", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.BorderCount + _context.GenerateElementInfos.BorderCount).ToString()); + GenerateBorder(); + _context.NewXmlWriter.WriteFullEndElement(); + + if (!_context.OldElementInfos.ExistsCellStyleXfs) + { + GenerateCellStyleXfs(); + } + } + + protected virtual async Task GenerateBordersAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "borders", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.BorderCount + _context.GenerateElementInfos.BorderCount).ToString()); + await GenerateBorderAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + if (!_context.OldElementInfos.ExistsCellStyleXfs) + { + await GenerateCellStyleXfsAsync(); + } + } + + protected abstract void GenerateBorder(); + + protected abstract Task GenerateBorderAsync(); + + protected virtual void GenerateCellStyleXfs() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "cellStyleXfs", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.CellStyleXfCount + _context.GenerateElementInfos.CellStyleXfCount).ToString()); + GenerateCellStyleXf(); + _context.NewXmlWriter.WriteFullEndElement(); + + if (!_context.OldElementInfos.ExistsCellXfs) + { + GenerateCellXfs(); + } + } + + protected virtual async Task GenerateCellStyleXfsAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "cellStyleXfs", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.CellStyleXfCount + _context.GenerateElementInfos.CellStyleXfCount).ToString()); + await GenerateCellStyleXfAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + + if (!_context.OldElementInfos.ExistsCellXfs) + { + await GenerateCellXfsAsync(); + } + } + + protected abstract void GenerateCellStyleXf(); + + protected abstract Task GenerateCellStyleXfAsync(); + + protected virtual void GenerateCellXfs() + { + _context.NewXmlWriter.WriteStartElement(_context.OldXmlReader.Prefix, "cellXfs", _context.OldXmlReader.NamespaceURI); + _context.NewXmlWriter.WriteAttributeString("count", (_context.OldElementInfos.CellXfCount + _context.GenerateElementInfos.CellXfCount + _context.CustomFormatCount).ToString()); + GenerateCellXf(); + _context.NewXmlWriter.WriteFullEndElement(); + } + + protected virtual async Task GenerateCellXfsAsync() + { + await _context.NewXmlWriter.WriteStartElementAsync(_context.OldXmlReader.Prefix, "cellXfs", _context.OldXmlReader.NamespaceURI); + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "count", null, (_context.OldElementInfos.CellXfCount + _context.GenerateElementInfos.CellXfCount + _context.CustomFormatCount).ToString()); + await GenerateCellXfAsync(); + await _context.NewXmlWriter.WriteFullEndElementAsync(); + } + + protected abstract void GenerateCellXf(); + + protected abstract Task GenerateCellXfAsync(); + + private Dictionary GetCellXfIdMap() + { + var result = new Dictionary(); + for (int i = 0; i < _context.GenerateElementInfos.CellXfCount; i++) + { + result.Add(i.ToString(), (_context.OldElementInfos.CellXfCount + i).ToString()); + } + return result; + } + } +} diff --git a/src/MiniExcel/OpenXml/Styles/SheetStyleElementInfos.cs b/src/MiniExcel/OpenXml/Styles/SheetStyleElementInfos.cs new file mode 100644 index 00000000..4193b64b --- /dev/null +++ b/src/MiniExcel/OpenXml/Styles/SheetStyleElementInfos.cs @@ -0,0 +1,29 @@ +namespace MiniExcelLibs.OpenXml.Styles +{ + public class SheetStyleElementInfos + { + public bool ExistsNumFmts { get; set; } + + public int NumFmtCount { get; set; } + + public bool ExistsFonts { get; set; } + + public int FontCount { get; set; } + + public bool ExistsFills { get; set; } + + public int FillCount { get; set; } + + public bool ExistsBorders { get; set; } + + public int BorderCount { get; set; } + + public bool ExistsCellStyleXfs { get; set; } + + public int CellStyleXfCount { get; set; } + + public bool ExistsCellXfs { get; set; } + + public int CellXfCount { get; set; } + } +} diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs index 07d68332..815006e4 100644 --- a/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs +++ b/tests/MiniExcelTests/MiniExcelOpenXmlAsyncTests.cs @@ -1516,5 +1516,184 @@ public async Task SaveAsByMiniExcelDataReader() Assert.True(results.Last().Column1 == "Github"); Assert.True(results.Last().Column2 == 2); } + + [Fact] + public async Task InsertSheetTest() + { + var now = DateTime.Now; + var path = PathHelper.GetTempPath(); + { + var table = new DataTable(); + { + table.Columns.Add("a", typeof(string)); + table.Columns.Add("b", typeof(decimal)); + table.Columns.Add("c", typeof(bool)); + table.Columns.Add("d", typeof(DateTime)); + table.Rows.Add(@"""<>+-*//}{\\n", 1234567890, true, now); + table.Rows.Add(@"Hello World", -1234567890, false, now.Date); + } + + await MiniExcel.InsertAsync(path, table, sheetName: "Sheet1"); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet1 = p.Workbook.Worksheets[0]; + + Assert.True(sheet1.Cells["A1"].Value.ToString() == "a"); + Assert.True(sheet1.Cells["B1"].Value.ToString() == "b"); + Assert.True(sheet1.Cells["C1"].Value.ToString() == "c"); + Assert.True(sheet1.Cells["D1"].Value.ToString() == "d"); + + Assert.True(sheet1.Cells["A2"].Value.ToString() == @"""<>+-*//}{\\n"); + Assert.True(sheet1.Cells["B2"].Value.ToString() == @"1234567890"); + Assert.True(sheet1.Cells["C2"].Value.ToString() == true.ToString()); + Assert.True(sheet1.Cells["D2"].Value.ToString() == now.ToString()); + + Assert.True(sheet1.Name == "Sheet1"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(int)); + table.Rows.Add("MiniExcel", 1); + table.Rows.Add("Github", 2); + } + + await MiniExcel.InsertAsync(path, table, sheetName: "Sheet2"); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet2 = p.Workbook.Worksheets[1]; + + Assert.True(sheet2.Cells["A1"].Value.ToString() == "Column1"); + Assert.True(sheet2.Cells["B1"].Value.ToString() == "Column2"); + + Assert.True(sheet2.Cells["A2"].Value.ToString() == "MiniExcel"); + Assert.True(sheet2.Cells["B2"].Value.ToString() == "1"); + + Assert.True(sheet2.Cells["A3"].Value.ToString() == "Github"); + Assert.True(sheet2.Cells["B3"].Value.ToString() == "2"); + + Assert.True(sheet2.Name == "Sheet2"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(DateTime)); + table.Rows.Add("Test", now); + } + + await MiniExcel.InsertAsync(path, table, sheetName: "Sheet2", printHeader: false, configuration: new OpenXmlConfiguration + { + FastMode = true, + AutoFilter = false, + TableStyles = TableStyles.None, + DynamicColumns = new[] + { + new DynamicExcelColumn("Column2") + { + Name = "Its Date", + Index = 1, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", + } + } + }, overwriteSheet: true); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet2 = p.Workbook.Worksheets[1]; + + Assert.True(sheet2.Cells["A1"].Value.ToString() == "Test"); + Assert.True(sheet2.Cells["B1"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + Assert.True(sheet2.Name == "Sheet2"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(DateTime)); + table.Rows.Add("MiniExcel", now); + table.Rows.Add("Github", now); + } + + await MiniExcel.InsertAsync(path, table, sheetName: "Sheet3", configuration: new OpenXmlConfiguration + { + FastMode = true, + AutoFilter = false, + TableStyles = TableStyles.None, + DynamicColumns = new[] + { + new DynamicExcelColumn("Column2") + { + Name = "Its Date", + Index = 1, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", + } + } + }); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet3 = p.Workbook.Worksheets[2]; + + Assert.True(sheet3.Cells["A1"].Value.ToString() == "Column1"); + Assert.True(sheet3.Cells["B1"].Value.ToString() == "Its Date"); + + Assert.True(sheet3.Cells["A2"].Value.ToString() == "MiniExcel"); + Assert.True(sheet3.Cells["B2"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + + Assert.True(sheet3.Cells["A3"].Value.ToString() == "Github"); + Assert.True(sheet3.Cells["B3"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + + Assert.True(sheet3.Name == "Sheet3"); + } + } + } + + [Fact] + public async Task InsertCsvTest() + { + var path = PathHelper.GetTempFilePath("csv"); + { + var value = new[] { + new { ID=1,Name ="Jack",InDate=new DateTime(2021,01,03)}, + new { ID=2,Name ="Henry",InDate=new DateTime(2020,05,03)}, + }; + await MiniExcel.SaveAsAsync(path, value); + var content = await File.ReadAllTextAsync(path); + Assert.Contains(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +", content); + } + { + var value = new { ID = 3, Name = "Mike", InDate = new DateTime(2021, 04, 23) }; + await MiniExcel.InsertAsync(path, value); + var content = await File.ReadAllTextAsync(path); + Assert.Equal(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +3,Mike,""2021-04-23 00:00:00"" +", content); + } + { + var value = new[] { + new { ID=4,Name ="Frank",InDate=new DateTime(2021,06,07)}, + new { ID=5,Name ="Gloria",InDate=new DateTime(2022,05,03)}, + }; + await MiniExcel.InsertAsync(path, value); + var content = await File.ReadAllTextAsync(path); + Assert.Equal(@"ID,Name,InDate +1,Jack,""2021-01-03 00:00:00"" +2,Henry,""2020-05-03 00:00:00"" +3,Mike,""2021-04-23 00:00:00"" +4,Frank,""2021-06-07 00:00:00"" +5,Gloria,""2022-05-03 00:00:00"" +", content); + } + } } } \ No newline at end of file diff --git a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs index 82e01932..ad9e0f65 100644 --- a/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs +++ b/tests/MiniExcelTests/MiniExcelOpenXmlTests.cs @@ -1455,5 +1455,141 @@ public void DynamicColumnsConfigurationIsUsedWhenCreatingExcelUsingDataTable() Assert.Equal(onlyDate.ToDateTime(TimeOnly.MinValue), (DateTime)rows[1]["Column4"]); } } + + [Fact] + public void InsertSheetTest() + { + var now = DateTime.Now; + var path = PathHelper.GetTempPath(); + { + var table = new DataTable(); + { + table.Columns.Add("a", typeof(string)); + table.Columns.Add("b", typeof(decimal)); + table.Columns.Add("c", typeof(bool)); + table.Columns.Add("d", typeof(DateTime)); + table.Rows.Add(@"""<>+-*//}{\\n", 1234567890, true, now); + table.Rows.Add(@"Hello World", -1234567890, false, now.Date); + } + + MiniExcel.Insert(path, table, sheetName: "Sheet1"); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet1 = p.Workbook.Worksheets[0]; + + Assert.True(sheet1.Cells["A1"].Value.ToString() == "a"); + Assert.True(sheet1.Cells["B1"].Value.ToString() == "b"); + Assert.True(sheet1.Cells["C1"].Value.ToString() == "c"); + Assert.True(sheet1.Cells["D1"].Value.ToString() == "d"); + + Assert.True(sheet1.Cells["A2"].Value.ToString() == @"""<>+-*//}{\\n"); + Assert.True(sheet1.Cells["B2"].Value.ToString() == @"1234567890"); + Assert.True(sheet1.Cells["C2"].Value.ToString() == true.ToString()); + Assert.True(sheet1.Cells["D2"].Value.ToString() == now.ToString()); + + Assert.True(sheet1.Name == "Sheet1"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(int)); + table.Rows.Add("MiniExcel", 1); + table.Rows.Add("Github", 2); + } + + MiniExcel.Insert(path, table, sheetName: "Sheet2"); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet2 = p.Workbook.Worksheets[1]; + + Assert.True(sheet2.Cells["A1"].Value.ToString() == "Column1"); + Assert.True(sheet2.Cells["B1"].Value.ToString() == "Column2"); + + Assert.True(sheet2.Cells["A2"].Value.ToString() == "MiniExcel"); + Assert.True(sheet2.Cells["B2"].Value.ToString() == "1"); + + Assert.True(sheet2.Cells["A3"].Value.ToString() == "Github"); + Assert.True(sheet2.Cells["B3"].Value.ToString() == "2"); + + Assert.True(sheet2.Name == "Sheet2"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(DateTime)); + table.Rows.Add("Test", now); + } + + MiniExcel.Insert(path, table, sheetName: "Sheet2", printHeader: false, configuration: new OpenXmlConfiguration + { + FastMode = true, + AutoFilter = false, + TableStyles = TableStyles.None, + DynamicColumns = new[] + { + new DynamicExcelColumn("Column2") + { + Name = "Its Date", + Index = 1, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", + } + } + }, overwriteSheet: true); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet2 = p.Workbook.Worksheets[1]; + + Assert.True(sheet2.Cells["A1"].Value.ToString() == "Test"); + Assert.True(sheet2.Cells["B1"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + Assert.True(sheet2.Name == "Sheet2"); + } + } + { + var table = new DataTable(); + { + table.Columns.Add("Column1", typeof(string)); + table.Columns.Add("Column2", typeof(DateTime)); + table.Rows.Add("MiniExcel", now); + table.Rows.Add("Github", now); + } + + MiniExcel.Insert(path, table, sheetName: "Sheet3", configuration: new OpenXmlConfiguration + { + FastMode = true, + AutoFilter = false, + TableStyles = TableStyles.None, + DynamicColumns = new[] + { + new DynamicExcelColumn("Column2") + { + Name = "Its Date", + Index = 1, + Width = 150, + Format = "dd.mm.yyyy hh:mm:ss", + } + } + }); + using (var p = new ExcelPackage(new FileInfo(path))) + { + var sheet3 = p.Workbook.Worksheets[2]; + + Assert.True(sheet3.Cells["A1"].Value.ToString() == "Column1"); + Assert.True(sheet3.Cells["B1"].Value.ToString() == "Its Date"); + + Assert.True(sheet3.Cells["A2"].Value.ToString() == "MiniExcel"); + Assert.True(sheet3.Cells["B2"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + + Assert.True(sheet3.Cells["A3"].Value.ToString() == "Github"); + Assert.True(sheet3.Cells["B3"].Text == now.ToString("dd.MM.yyyy HH:mm:ss")); + + Assert.True(sheet3.Name == "Sheet3"); + } + } + } } } \ No newline at end of file