From 6fc61a72fad460c594c66828b11d3fa53d4768ca Mon Sep 17 00:00:00 2001 From: Michele Bastione Date: Sun, 19 Apr 2026 14:57:03 +0200 Subject: [PATCH 1/3] Increased version to 1.43.1 In preparation for the next release, the project file has been cleaned up a little and the benchmark.yml workflow file has been revised. --- .github/workflows/benchmark.yml | 2 +- src/MiniExcel/MiniExcelLibs.csproj | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index f73804d7..6a164511 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -119,4 +119,4 @@ jobs: git config user.email github-actions@github.com git add ./*.md git commit -am "Automated benchmark report - ${{ github.ref_name }}" - git push origin v1.x-maintenance --force-with-lease + git push origin HEAD:v1.x-maintenance --force-with-lease diff --git a/src/MiniExcel/MiniExcelLibs.csproj b/src/MiniExcel/MiniExcelLibs.csproj index e3642133..eb884884 100644 --- a/src/MiniExcel/MiniExcelLibs.csproj +++ b/src/MiniExcel/MiniExcelLibs.csproj @@ -1,11 +1,12 @@  net45;netstandard2.0;net8.0;net9.0;net10.0 - 1.43.0 - - - 8 + 1.43.1 + 14 + enable + MiniExcelLibs + MiniExcel Mini-Software @@ -21,13 +22,12 @@ Todo : https://github.com/mini-software/MiniExcel/projects/1?fullscreen=trueMiniExcel Wei Lin, 2021 onwards en - https://raw.githubusercontent.com/mini-software/MiniExcel/master/LICENSE - MiniExcelLibs + https://raw.githubusercontent.com/mini-software/MiniExcel/master/LICENSE Apache-2.0 https://github.com/mini-software/MiniExcel https://github.com/mini-software/MiniExcel icon.png - Please Check [Release Notes](https://github.com/mini-software/MiniExcel/tree/master/docs) + https://github.com/mini-software/MiniExcel/tree/master/docs Github miniexcel.snk True @@ -38,17 +38,14 @@ Todo : https://github.com/mini-software/MiniExcel/projects/1?fullscreen=trueREADME.md - - - - - - - + + + + From 3bebaa4345b38c52c273bb1dff427e4bac60b698 Mon Sep 17 00:00:00 2001 From: Michele Bastione Date: Sun, 19 Apr 2026 15:12:32 +0200 Subject: [PATCH 2/3] Converted project to utilize implicit usings and file-scoped namespaces This makes the v1.x branch less cluttered and more mantainable. Also the purposeless MiniEcel.Code.cs file has been deleted. --- .../Attributes/ExcelColumnAttribute.cs | 92 +- .../Attributes/ExcelColumnIndexAttribute.cs | 44 +- .../Attributes/ExcelColumnNameAttribute.cs | 22 +- .../Attributes/ExcelColumnWidthAttribute.cs | 17 +- .../Attributes/ExcelFormatAttribute.cs | 15 +- .../Attributes/ExcelHiddenAttribute.cs | 15 +- .../Attributes/ExcelIgnoreAttribute.cs | 15 +- .../Attributes/ExcelSheetAttribute.cs | 26 +- src/MiniExcel/Csv/CsvConfiguration.cs | 34 +- src/MiniExcel/Csv/CsvHelpers.cs | 39 +- src/MiniExcel/Csv/CsvReader.cs | 283 +- src/MiniExcel/Csv/CsvWriter.cs | 397 ++- src/MiniExcel/ExcelFactory.cs | 91 +- src/MiniExcel/ExcelType.cs | 23 +- .../ExcelColumnNotFoundException.cs | 37 +- .../Exceptions/ExcelInvalidCastException.cs | 27 +- .../MiniExcelNotSerializableException.cs | 18 +- src/MiniExcel/IConfiguration.cs | 65 +- src/MiniExcel/IExcelReader.cs | 36 +- src/MiniExcel/IExcelTemplate.cs | 37 +- src/MiniExcel/IExcelWriter.cs | 18 +- src/MiniExcel/IMiniExcelDataReader.cs | 25 +- src/MiniExcel/MiniExcel.Async.cs | 267 +- src/MiniExcel/MiniExcel.Code.cs | 7 - src/MiniExcel/MiniExcel.cs | 549 ++-- src/MiniExcel/MiniExcelDataReader.cs | 153 +- src/MiniExcel/MiniExcelDataReaderBase.cs | 668 +++-- src/MiniExcel/MiniExcelTask.cs | 43 +- src/MiniExcel/OpenXml/Config.cs | 17 +- .../OpenXml/Constants/ExcelContentTypes.cs | 21 +- .../OpenXml/Constants/ExcelFileNames.cs | 27 +- src/MiniExcel/OpenXml/Constants/ExcelXml.cs | 66 +- .../OpenXml/Constants/WorksheetXml.cs | 99 +- .../OpenXml/ExcelOpenXmlSheetReader.Async.cs | 55 +- .../OpenXml/ExcelOpenXmlSheetReader.cs | 1460 +++++----- .../OpenXml/ExcelOpenXmlSheetWriter.Async.cs | 838 +++--- .../ExcelOpenXmlSheetWriter.DefaultOpenXml.cs | 645 +++-- .../OpenXml/ExcelOpenXmlSheetWriter.cs | 795 +++--- src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs | 322 ++- src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs | 175 +- src/MiniExcel/OpenXml/ExcelWidthCollection.cs | 107 +- src/MiniExcel/OpenXml/MergeCells.cs | 13 +- .../OpenXml/MiniExcelAsyncStreamWriter.cs | 117 +- .../OpenXml/MiniExcelStreamWriter.cs | 105 +- src/MiniExcel/OpenXml/Models/DrawingDto.cs | 9 +- src/MiniExcel/OpenXml/Models/ExcelRange.cs | 47 +- src/MiniExcel/OpenXml/Models/FileDto.cs | 25 +- src/MiniExcel/OpenXml/Models/SheetDto.cs | 17 +- src/MiniExcel/OpenXml/OpenXmlConfiguration.cs | 66 +- src/MiniExcel/OpenXml/OpenXmlStyleOptions.cs | 59 +- .../OpenXml/SharedStringsDiskCache.cs | 250 +- src/MiniExcel/OpenXml/SheetInfo.cs | 63 +- src/MiniExcel/OpenXml/SheetRecord.cs | 53 +- .../Styles/DefaultSheetStyleBuilder.cs | 2056 +++++++------- .../OpenXml/Styles/ISheetStyleBuilder.cs | 14 +- .../Styles/MinimalSheetStyleBuilder.cs | 339 ++- .../OpenXml/Styles/SheetStyleBuildContext.cs | 511 ++-- .../OpenXml/Styles/SheetStyleBuildResult.cs | 17 +- .../OpenXml/Styles/SheetStyleBuilderBase.cs | 942 ++++--- .../OpenXml/Styles/SheetStyleBuilderHelper.cs | 31 +- .../OpenXml/Styles/SheetStyleElementInfos.cs | 33 +- src/MiniExcel/OpenXml/TableStyles.cs | 11 +- src/MiniExcel/Picture/MiniExcelPicture.cs | 23 +- .../Picture/MiniExcelPictureImplement.cs | 590 ++-- src/MiniExcel/Reflection/MemberGetter.cs | 76 +- src/MiniExcel/Reflection/MemberSetter.cs | 46 +- src/MiniExcel/Reflection/Property.cs | 91 +- .../ExcelOpenXmlTemplate.Impl.cs | 2397 ++++++++--------- .../ExcelOpenXmlTemplate.MergeCells.cs | 100 +- .../SaveByTemplate/ExcelOpenXmlTemplate.cs | 275 +- .../SaveByTemplate/IInputValueExtractor.cs | 9 +- .../SaveByTemplate/InputValueExtractor.cs | 59 +- src/MiniExcel/Utils/AttributeExtension.cs | 75 +- src/MiniExcel/Utils/ColumnHelper.cs | 94 +- src/MiniExcel/Utils/CustomPropertyHelper.cs | 662 +++-- src/MiniExcel/Utils/DateTimeHelper.cs | 35 +- src/MiniExcel/Utils/DictionaryHelper.cs | 42 +- src/MiniExcel/Utils/ExcelNumberFormat.cs | 1732 ++++++------ src/MiniExcel/Utils/ExcelTypeHelper.cs | 74 +- src/MiniExcel/Utils/FileHelper.cs | 11 +- src/MiniExcel/Utils/ImageHelper.cs | 75 +- src/MiniExcel/Utils/ListHelper.cs | 33 +- src/MiniExcel/Utils/ReferenceHelper.cs | 155 +- src/MiniExcel/Utils/StringHelper.cs | 101 +- src/MiniExcel/Utils/TypeHelper.cs | 406 ++- src/MiniExcel/Utils/XmlEncoder.cs | 65 +- src/MiniExcel/Utils/XmlReaderHelper.cs | 142 +- src/MiniExcel/Utils/calChainHelper.cs | 53 +- .../AsyncEnumerableWriteAdapter.cs | 125 +- .../WriteAdapter/DataReaderWriteAdapter.cs | 93 +- .../WriteAdapter/DataTableWriteAdapter.cs | 75 +- .../WriteAdapter/EnumerableWriteAdapter.cs | 171 +- .../IAsyncMiniExcelWriteAdapter.cs | 3 - .../WriteAdapter/IMiniExcelWriteAdapter.cs | 39 +- .../MiniExcelDataReaderWriteAdapter.cs | 100 +- .../MiniExcelWriteAdapterFactory.cs | 62 +- src/MiniExcel/Zip/ExcelOpenXmlZip.cs | 132 +- src/MiniExcel/Zip/MiniExcelZipArchive.cs | 27 +- src/MiniExcel/Zip/ZipPackageInfo.cs | 19 +- 99 files changed, 10192 insertions(+), 10543 deletions(-) delete mode 100644 src/MiniExcel/MiniExcel.Code.cs diff --git a/src/MiniExcel/Attributes/ExcelColumnAttribute.cs b/src/MiniExcel/Attributes/ExcelColumnAttribute.cs index 70d0c203..89c78efb 100644 --- a/src/MiniExcel/Attributes/ExcelColumnAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelColumnAttribute.cs @@ -1,58 +1,56 @@ using MiniExcelLibs.Utils; -using System; -namespace MiniExcelLibs.Attributes +namespace MiniExcelLibs.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelColumnAttribute : Attribute { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelColumnAttribute : Attribute + private int _index = -1; + private string _xName; + + internal int FormatId { get; set; } = -1; + + public string Name { get; set; } + public string[] Aliases { get; set; } + public double Width { get; set; } = 8.42857143; + public string Format { get; set; } + public bool Hidden { get; set; } + public bool Ignore { get; set; } + public ColumnType Type { get; set; } = ColumnType.Value; + + public int Index { - private int _index = -1; - private string _xName; - - internal int FormatId { get; set; } = -1; - - public string Name { get; set; } - public string[] Aliases { get; set; } - public double Width { get; set; } = 8.42857143; - public string Format { get; set; } - public bool Hidden { get; set; } - public bool Ignore { get; set; } - public ColumnType Type { get; set; } = ColumnType.Value; - - public int Index - { - get => _index; - set => Init(value); - } - - public string IndexName - { - get => _xName; - set => Init(ColumnHelper.GetColumnIndex(value), value); - } - - private void Init(int index, string columnName = null) - { - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index), index, $"Column index {index} must be greater or equal to zero."); - - if (_xName == null) - _xName = columnName ?? ColumnHelper.GetAlphabetColumnName(index); - - _index = index; - } + get => _index; + set => Init(value); } - public enum ColumnType { Value, Formula } + public string IndexName + { + get => _xName; + set => Init(ColumnHelper.GetColumnIndex(value), value); + } - public class DynamicExcelColumn : ExcelColumnAttribute + private void Init(int index, string columnName = null) { - public string Key { get; set; } - public Func CustomFormatter { get; set; } + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index), index, $"Column index {index} must be greater or equal to zero."); - public DynamicExcelColumn(string key) - { - Key = key; - } + if (_xName == null) + _xName = columnName ?? ColumnHelper.GetAlphabetColumnName(index); + + _index = index; + } +} + +public enum ColumnType { Value, Formula } + +public class DynamicExcelColumn : ExcelColumnAttribute +{ + public string Key { get; set; } + public Func CustomFormatter { get; set; } + + public DynamicExcelColumn(string key) + { + Key = key; } } \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelColumnIndexAttribute.cs b/src/MiniExcel/Attributes/ExcelColumnIndexAttribute.cs index d576baa0..de22f57e 100644 --- a/src/MiniExcel/Attributes/ExcelColumnIndexAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelColumnIndexAttribute.cs @@ -1,29 +1,27 @@ -namespace MiniExcelLibs.Attributes +using MiniExcelLibs.Utils; + +namespace MiniExcelLibs.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelColumnIndexAttribute : Attribute { - using MiniExcelLibs.Utils; - using System; + public int ExcelColumnIndex { get; set; } + internal string ExcelXName { get; set; } + public ExcelColumnIndexAttribute(string columnName) => Init(ColumnHelper + .GetColumnIndex(columnName), columnName); + public ExcelColumnIndexAttribute(int columnIndex) => Init(columnIndex); - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelColumnIndexAttribute : Attribute + private void Init(int columnIndex, string columnName = null) { - public int ExcelColumnIndex { get; set; } - internal string ExcelXName { get; set; } - public ExcelColumnIndexAttribute(string columnName) => Init(ColumnHelper - .GetColumnIndex(columnName), columnName); - public ExcelColumnIndexAttribute(int columnIndex) => Init(columnIndex); - - private void Init(int columnIndex, string columnName = null) + if (columnIndex < 0) { - if (columnIndex < 0) - { - throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, $"Column index {columnIndex} must be greater or equal to zero."); - } - if (ExcelXName == null) - if (columnName != null) - ExcelXName = columnName; - else - ExcelXName = ColumnHelper.GetAlphabetColumnName(columnIndex); - ExcelColumnIndex = columnIndex; + throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, $"Column index {columnIndex} must be greater or equal to zero."); } + if (ExcelXName == null) + if (columnName != null) + ExcelXName = columnName; + else + ExcelXName = ColumnHelper.GetAlphabetColumnName(columnIndex); + ExcelColumnIndex = columnIndex; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelColumnNameAttribute.cs b/src/MiniExcel/Attributes/ExcelColumnNameAttribute.cs index eb16530b..a2d5af97 100644 --- a/src/MiniExcel/Attributes/ExcelColumnNameAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelColumnNameAttribute.cs @@ -1,15 +1,13 @@ -namespace MiniExcelLibs.Attributes +namespace MiniExcelLibs.Attributes; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelColumnNameAttribute : Attribute { - using System; - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelColumnNameAttribute : Attribute + public string ExcelColumnName { get; set; } + public string[] Aliases { get; set; } + public ExcelColumnNameAttribute(string excelColumnName, string[] aliases = null) { - public string ExcelColumnName { get; set; } - public string[] Aliases { get; set; } - public ExcelColumnNameAttribute(string excelColumnName, string[] aliases = null) - { - ExcelColumnName = excelColumnName; - Aliases = aliases; - } + ExcelColumnName = excelColumnName; + Aliases = aliases; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelColumnWidthAttribute.cs b/src/MiniExcel/Attributes/ExcelColumnWidthAttribute.cs index fd7dfcb5..c3ad02b2 100644 --- a/src/MiniExcel/Attributes/ExcelColumnWidthAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelColumnWidthAttribute.cs @@ -1,11 +1,8 @@ -namespace MiniExcelLibs.Attributes -{ - using System; +namespace MiniExcelLibs.Attributes; - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelColumnWidthAttribute : Attribute - { - public double ExcelColumnWidth { get; set; } - public ExcelColumnWidthAttribute(double excelColumnWidth) => ExcelColumnWidth = excelColumnWidth; - } -} +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelColumnWidthAttribute : Attribute +{ + public double ExcelColumnWidth { get; set; } + public ExcelColumnWidthAttribute(double excelColumnWidth) => ExcelColumnWidth = excelColumnWidth; +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelFormatAttribute.cs b/src/MiniExcel/Attributes/ExcelFormatAttribute.cs index da503e5c..1fcf1d33 100644 --- a/src/MiniExcel/Attributes/ExcelFormatAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelFormatAttribute.cs @@ -1,11 +1,8 @@ -using System; +namespace MiniExcelLibs.Attributes; -namespace MiniExcelLibs.Attributes +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelFormatAttribute : Attribute { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelFormatAttribute : Attribute - { - public string Format { get; set; } - public ExcelFormatAttribute(string format) => Format = format; - } -} + public string Format { get; set; } + public ExcelFormatAttribute(string format) => Format = format; +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelHiddenAttribute.cs b/src/MiniExcel/Attributes/ExcelHiddenAttribute.cs index fcda446a..3ac08605 100644 --- a/src/MiniExcel/Attributes/ExcelHiddenAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelHiddenAttribute.cs @@ -1,11 +1,8 @@ -using System; +namespace MiniExcelLibs.Attributes; -namespace MiniExcelLibs.Attributes +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] +public class ExcelHiddenAttribute : Attribute { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - public class ExcelHiddenAttribute : Attribute - { - public bool ExcelHidden { get; set; } - public ExcelHiddenAttribute(bool excelHidden = true) => ExcelHidden = excelHidden; - } -} + public bool ExcelHidden { get; set; } + public ExcelHiddenAttribute(bool excelHidden = true) => ExcelHidden = excelHidden; +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelIgnoreAttribute.cs b/src/MiniExcel/Attributes/ExcelIgnoreAttribute.cs index 0331edc2..54696362 100644 --- a/src/MiniExcel/Attributes/ExcelIgnoreAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelIgnoreAttribute.cs @@ -1,11 +1,8 @@ -using System; +namespace MiniExcelLibs.Attributes; -namespace MiniExcelLibs.Attributes +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] +public class ExcelIgnoreAttribute : Attribute { - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)] - public class ExcelIgnoreAttribute : Attribute - { - public bool ExcelIgnore { get; set; } - public ExcelIgnoreAttribute(bool excelIgnore = true) => ExcelIgnore = excelIgnore; - } -} + public bool ExcelIgnore { get; set; } + public ExcelIgnoreAttribute(bool excelIgnore = true) => ExcelIgnore = excelIgnore; +} \ No newline at end of file diff --git a/src/MiniExcel/Attributes/ExcelSheetAttribute.cs b/src/MiniExcel/Attributes/ExcelSheetAttribute.cs index b3f1b7d6..4ad9019a 100644 --- a/src/MiniExcel/Attributes/ExcelSheetAttribute.cs +++ b/src/MiniExcel/Attributes/ExcelSheetAttribute.cs @@ -1,21 +1,19 @@ using MiniExcelLibs.OpenXml; -using System; -namespace MiniExcelLibs.Attributes +namespace MiniExcelLibs.Attributes; + +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] +public class ExcelSheetAttribute : Attribute { - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class ExcelSheetAttribute : Attribute - { - public string Name { get; set; } - public SheetState State { get; set; } = SheetState.Visible; - } + public string Name { get; set; } + public SheetState State { get; set; } = SheetState.Visible; +} - public class DynamicExcelSheet : ExcelSheetAttribute +public class DynamicExcelSheet : ExcelSheetAttribute +{ + public string Key { get; set; } + public DynamicExcelSheet(string key) { - public string Key { get; set; } - public DynamicExcelSheet(string key) - { - Key = key; - } + Key = key; } } \ No newline at end of file diff --git a/src/MiniExcel/Csv/CsvConfiguration.cs b/src/MiniExcel/Csv/CsvConfiguration.cs index 1d795c40..14886d0d 100644 --- a/src/MiniExcel/Csv/CsvConfiguration.cs +++ b/src/MiniExcel/Csv/CsvConfiguration.cs @@ -1,24 +1,20 @@ -using System; -using System.IO; using System.Text; -namespace MiniExcelLibs.Csv -{ - public class CsvConfiguration : Configuration - { - private static readonly Encoding DefaultEncoding = new UTF8Encoding(true); +namespace MiniExcelLibs.Csv; - public char Seperator { get; set; } = ','; - public string NewLine { get; set; } = "\r\n"; - public bool ReadLineBreaksWithinQuotes { get; set; } = true; - public bool ReadEmptyStringAsNull { get; set; } = false; - public bool AlwaysQuote { get; set; } = false; - public bool QuoteWhitespaces { get; set; } = true; - public Func SplitFn { get; set; } - public Func StreamReaderFunc { get; set; } = (stream) => new StreamReader(stream, DefaultEncoding); - public Func StreamWriterFunc { get; set; } = (stream) => new StreamWriter(stream, DefaultEncoding); +public class CsvConfiguration : Configuration +{ + private static readonly Encoding DefaultEncoding = new UTF8Encoding(true); - internal static readonly CsvConfiguration DefaultConfiguration = new CsvConfiguration(); - } -} + public char Seperator { get; set; } = ','; + public string NewLine { get; set; } = "\r\n"; + public bool ReadLineBreaksWithinQuotes { get; set; } = true; + public bool ReadEmptyStringAsNull { get; set; } = false; + public bool AlwaysQuote { get; set; } = false; + public bool QuoteWhitespaces { get; set; } = true; + public Func SplitFn { get; set; } + public Func StreamReaderFunc { get; set; } = (stream) => new StreamReader(stream, DefaultEncoding); + public Func StreamWriterFunc { get; set; } = (stream) => new StreamWriter(stream, DefaultEncoding); + internal static readonly CsvConfiguration DefaultConfiguration = new CsvConfiguration(); +} \ No newline at end of file diff --git a/src/MiniExcel/Csv/CsvHelpers.cs b/src/MiniExcel/Csv/CsvHelpers.cs index b6fd5a10..7db44caf 100644 --- a/src/MiniExcel/Csv/CsvHelpers.cs +++ b/src/MiniExcel/Csv/CsvHelpers.cs @@ -1,26 +1,25 @@ -namespace MiniExcelLibs.Csv +namespace MiniExcelLibs.Csv; + +internal static class CsvHelpers { - internal static class CsvHelpers + /// If content contains special characters then use "{value}" format + public static string ConvertToCsvValue(string value, CsvConfiguration configuration) { - /// If content contains special characters then use "{value}" format - public static string ConvertToCsvValue(string value, CsvConfiguration configuration) - { - if (value == null) - return string.Empty; + if (value == null) + return string.Empty; - if (value.Contains("\"")) - { - value = value.Replace("\"", "\"\""); - return $"\"{value}\""; - } + if (value.Contains("\"")) + { + value = value.Replace("\"", "\"\""); + return $"\"{value}\""; + } - var shouldQuote = configuration.AlwaysQuote || - (configuration.QuoteWhitespaces && value.Contains(" ")) || - value.Contains(configuration.Seperator.ToString()) || - value.Contains("\r") || - value.Contains("\n"); + var shouldQuote = configuration.AlwaysQuote || + (configuration.QuoteWhitespaces && value.Contains(" ")) || + value.Contains(configuration.Seperator.ToString()) || + value.Contains("\r") || + value.Contains("\n"); - return shouldQuote ? $"\"{value}\"" : value; - } + return shouldQuote ? $"\"{value}\"" : value; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/Csv/CsvReader.cs b/src/MiniExcel/Csv/CsvReader.cs index 36ce18e5..c9096d23 100644 --- a/src/MiniExcel/Csv/CsvReader.cs +++ b/src/MiniExcel/Csv/CsvReader.cs @@ -1,190 +1,183 @@ using MiniExcelLibs.Exceptions; using MiniExcelLibs.OpenXml; using MiniExcelLibs.Utils; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -namespace MiniExcelLibs.Csv +namespace MiniExcelLibs.Csv; + +internal class CsvReader : IExcelReader { - internal class CsvReader : IExcelReader - { - private Stream _stream; - private CsvConfiguration _config; + private Stream _stream; + private CsvConfiguration _config; - public CsvReader(Stream stream, IConfiguration configuration) - { - _stream = stream; - _config = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration; - } + public CsvReader(Stream stream, IConfiguration configuration) + { + _stream = stream; + _config = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration; + } - public IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell) - { - if (startCell != "A1") - throw new NotImplementedException("CSV does not implement parameter startCell"); + public IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell) + { + if (startCell != "A1") + throw new NotImplementedException("CSV does not implement parameter startCell"); - if (_stream.CanSeek) - _stream.Position = 0; + if (_stream.CanSeek) + _stream.Position = 0; - var reader = _config.StreamReaderFunc(_stream); - var firstRow = true; - var headRows = new Dictionary(); + var reader = _config.StreamReaderFunc(_stream); + var firstRow = true; + var headRows = new Dictionary(); - string row; - for (var rowIndex = 1; (row = reader.ReadLine()) != null; rowIndex++) - { - if (string.IsNullOrWhiteSpace(row)) - continue; + string row; + for (var rowIndex = 1; (row = reader.ReadLine()) != null; rowIndex++) + { + if (string.IsNullOrWhiteSpace(row)) + continue; - string finalRow = row; - if (_config.ReadLineBreaksWithinQuotes) + string finalRow = row; + if (_config.ReadLineBreaksWithinQuotes) + { + while (finalRow.Count(c => c == '"') % 2 != 0) { - while (finalRow.Count(c => c == '"') % 2 != 0) + var nextPart = reader.ReadLine(); + if (nextPart == null) { - var nextPart = reader.ReadLine(); - if (nextPart == null) - { - break; - } - finalRow = string.Concat(finalRow, _config.NewLine, nextPart); + break; } + finalRow = string.Concat(finalRow, _config.NewLine, nextPart); } - var read = Split(finalRow); + } + var read = Split(finalRow); - // invalid row check - if (read.Length < headRows.Count) - { - var colIndex = read.Length; - var headers = headRows.ToDictionary(x => x.Value, x => x.Key); - var rowValues = read - .Select((x, i) => new KeyValuePair(headRows[i], x)) - .ToDictionary(x => x.Key, x => x.Value); + // invalid row check + if (read.Length < headRows.Count) + { + var colIndex = read.Length; + var headers = headRows.ToDictionary(x => x.Value, x => x.Key); + var rowValues = read + .Select((x, i) => new KeyValuePair(headRows[i], x)) + .ToDictionary(x => x.Key, x => x.Value); - throw new ExcelColumnNotFoundException(columnIndex: null, headRows[colIndex], null, rowIndex, headers, rowValues, $"Csv read error: Column {colIndex} not found in Row {rowIndex}"); - } + throw new ExcelColumnNotFoundException(columnIndex: null, headRows[colIndex], null, rowIndex, headers, rowValues, $"Csv read error: Column {colIndex} not found in Row {rowIndex}"); + } - //header - if (useHeaderRow) + //header + if (useHeaderRow) + { + if (firstRow) { - if (firstRow) - { - firstRow = false; - for (int i = 0; i <= read.Length - 1; i++) - headRows.Add(i, read[i]); - continue; - } - - var headCell = CustomPropertyHelper.GetEmptyExpandoObject(headRows); + firstRow = false; for (int i = 0; i <= read.Length - 1; i++) - headCell[headRows[i]] = read[i]; - - yield return headCell; + headRows.Add(i, read[i]); continue; } - //body - if (firstRow) // record first row as reference - { - firstRow = false; - for (int i = 0; i <= read.Length - 1; i++) - headRows.Add(i, $"c{i + 1}"); - } + var headCell = CustomPropertyHelper.GetEmptyExpandoObject(headRows); + for (int i = 0; i <= read.Length - 1; i++) + headCell[headRows[i]] = read[i]; - var cell = CustomPropertyHelper.GetEmptyExpandoObject(read.Length - 1, 0); - if (_config.ReadEmptyStringAsNull) - { - for (int i = 0; i <= read.Length - 1; i++) - cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]?.Length == 0 ? null : read[i]; - } - else - { - for (int i = 0; i <= read.Length - 1; i++) - cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]; - } + yield return headCell; + continue; + } - yield return cell; + //body + if (firstRow) // record first row as reference + { + firstRow = false; + for (int i = 0; i <= read.Length - 1; i++) + headRows.Add(i, $"c{i + 1}"); } - } - public IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new() - { - var dynamicRecords = Query(false, sheetName, startCell); - return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, startCell, hasHeader, _config); - } + var cell = CustomPropertyHelper.GetEmptyExpandoObject(read.Length - 1, 0); + if (_config.ReadEmptyStringAsNull) + { + for (int i = 0; i <= read.Length - 1; i++) + cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]?.Length == 0 ? null : read[i]; + } + else + { + for (int i = 0; i <= read.Length - 1; i++) + cell[ColumnHelper.GetAlphabetColumnName(i)] = read[i]; + } - public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell) - { - throw new NotImplementedException("CSV does not implement QueryRange"); + yield return cell; } + } - public IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new() - { - var dynamicRecords = QueryRange(false, sheetName, startCell, endCell); - return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, startCell, hasHeader, this._config); - } + public IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new() + { + var dynamicRecords = Query(false, sheetName, startCell); + return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, startCell, hasHeader, _config); + } - public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) - { - throw new NotImplementedException("CSV does not implement QueryRange"); - } + public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell) + { + throw new NotImplementedException("CSV does not implement QueryRange"); + } - public IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new() - { - var dynamicRecords = QueryRange(false, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); - return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, ReferenceHelper.ConvertXyToCell(startRowIndex, startColumnIndex), hasHeader, this._config); - } + public IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new() + { + var dynamicRecords = QueryRange(false, sheetName, startCell, endCell); + return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, startCell, hasHeader, this._config); + } - public Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default) - { - return Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken); - } + public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) + { + throw new NotImplementedException("CSV does not implement QueryRange"); + } - public async Task> QueryAsync(string sheetName, string startCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => Query(sheetName, startCell, hasHeader), cancellationToken).ConfigureAwait(false); - } + public IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new() + { + var dynamicRecords = QueryRange(false, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); + return ExcelOpenXmlSheetReader.QueryImpl(dynamicRecords, ReferenceHelper.ConvertXyToCell(startRowIndex, startColumnIndex), hasHeader, this._config); + } - public Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCel, CancellationToken cancellationToken = default) - { - return Task.Run(() => QueryRange(useHeaderRow, sheetName, startCell, endCel), cancellationToken); - } + public Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default) + { + return Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken); + } - public async Task> QueryRangeAsync(string sheetName, string startCell, string endCel, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => QueryRange(sheetName, startCell, endCel, hasHeader), cancellationToken).ConfigureAwait(false); - } + public async Task> QueryAsync(string sheetName, string startCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => Query(sheetName, startCell, hasHeader), cancellationToken).ConfigureAwait(false); + } - public Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default) - { - return Task.Run(() => QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), cancellationToken); - } + public Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCel, CancellationToken cancellationToken = default) + { + return Task.Run(() => QueryRange(useHeaderRow, sheetName, startCell, endCel), cancellationToken); + } - public async Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => QueryRange(sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, hasHeader), cancellationToken).ConfigureAwait(false); - } + public async Task> QueryRangeAsync(string sheetName, string startCell, string endCel, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => QueryRange(sheetName, startCell, endCel, hasHeader), cancellationToken).ConfigureAwait(false); + } + + public Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default) + { + return Task.Run(() => QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), cancellationToken); + } + + public async Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => QueryRange(sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, hasHeader), cancellationToken).ConfigureAwait(false); + } - private string[] Split(string row) + private string[] Split(string row) + { + if (_config.SplitFn != null) { - if (_config.SplitFn != null) - { - return _config.SplitFn(row); - } - else - { - //this code from S.O : https://stackoverflow.com/a/11365961/9131476 - return Regex.Split(row, $"[\t{_config.Seperator}](?=(?:[^\"]|\"[^\"]*\")*$)") - .Select(s => Regex.Replace(s.Replace("\"\"", "\""), "^\"|\"$", "")) - .ToArray(); - } + return _config.SplitFn(row); } - - public void Dispose() + else { + //this code from S.O : https://stackoverflow.com/a/11365961/9131476 + return Regex.Split(row, $"[\t{_config.Seperator}](?=(?:[^\"]|\"[^\"]*\")*$)") + .Select(s => Regex.Replace(s.Replace("\"\"", "\""), "^\"|\"$", "")) + .ToArray(); } } -} + + public void Dispose() + { + } +} \ No newline at end of file diff --git a/src/MiniExcel/Csv/CsvWriter.cs b/src/MiniExcel/Csv/CsvWriter.cs index 9f4d7840..cc67e1cc 100644 --- a/src/MiniExcel/Csv/CsvWriter.cs +++ b/src/MiniExcel/Csv/CsvWriter.cs @@ -1,278 +1,271 @@ using MiniExcelLibs.Utils; using MiniExcelLibs.WriteAdapter; -using System; -using System.Collections.Generic; using System.Globalization; -using System.IO; -using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; -namespace MiniExcelLibs.Csv +namespace MiniExcelLibs.Csv; + +internal class CsvWriter : IExcelWriter, IDisposable { - internal class CsvWriter : IExcelWriter, IDisposable + private readonly StreamWriter _writer; + private readonly CsvConfiguration _configuration; + private readonly bool _printHeader; + private readonly object _value; + private bool _disposedValue; + + public CsvWriter(Stream stream, object value, IConfiguration configuration, bool printHeader) { - private readonly StreamWriter _writer; - private readonly CsvConfiguration _configuration; - private readonly bool _printHeader; - private readonly object _value; - private bool _disposedValue; + _configuration = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration; + _printHeader = printHeader; + _value = value; + _writer = _configuration.StreamWriterFunc(stream); + } - public CsvWriter(Stream stream, object value, IConfiguration configuration, bool printHeader) + public int[] SaveAs() + { + if (_value == null) { - _configuration = configuration == null ? CsvConfiguration.DefaultConfiguration : (CsvConfiguration)configuration; - _printHeader = printHeader; - _value = value; - _writer = _configuration.StreamWriterFunc(stream); + _writer.Write(""); + _writer.Flush(); + return new int[0]; } - public int[] SaveAs() - { - if (_value == null) - { - _writer.Write(""); - _writer.Flush(); - return new int[0]; - } + var rowsWritten = WriteValues(_value); + _writer.Flush(); - var rowsWritten = WriteValues(_value); - _writer.Flush(); + return new[] { rowsWritten }; + } - return new[] { rowsWritten }; - } + public int Insert(bool overwriteSheet = false) + { + return SaveAs().FirstOrDefault(); + } - public int Insert(bool overwriteSheet = false) - { - return SaveAs().FirstOrDefault(); - } + private void AppendColumn(StringBuilder rowBuilder, CellWriteInfo column) + { + rowBuilder.Append(CsvHelpers.ConvertToCsvValue(ToCsvString(column.Value, column.Prop), _configuration)); + rowBuilder.Append(_configuration.Seperator); + } - private void AppendColumn(StringBuilder rowBuilder, CellWriteInfo column) + private static void RemoveTrailingSeparator(StringBuilder rowBuilder) + { + if (rowBuilder.Length == 0) + return; + + rowBuilder.Remove(rowBuilder.Length - 1, 1); + } + + private string GetHeader(List props) => string.Join( + _configuration.Seperator.ToString(), + props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration))); + + private int WriteValues(object values) + { + var writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); + + var props = writeAdapter.GetColumns(); + if (props == null) { - rowBuilder.Append(CsvHelpers.ConvertToCsvValue(ToCsvString(column.Value, column.Prop), _configuration)); - rowBuilder.Append(_configuration.Seperator); + _writer.Write(_configuration.NewLine); + _writer.Flush(); + return 0; } - private static void RemoveTrailingSeparator(StringBuilder rowBuilder) + if (_printHeader) { - if (rowBuilder.Length == 0) - return; - - rowBuilder.Remove(rowBuilder.Length - 1, 1); + _writer.Write(GetHeader(props)); + _writer.Write(_configuration.NewLine); } - private string GetHeader(List props) => string.Join( - _configuration.Seperator.ToString(), - props.Select(s => CsvHelpers.ConvertToCsvValue(s?.ExcelColumnName, _configuration))); + if (writeAdapter == null) + return 0; + + var rowBuilder = new StringBuilder(); + var rowsWritten = 0; - private int WriteValues(object values) + foreach (var row in writeAdapter.GetRows(props)) { - var writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); - - var props = writeAdapter.GetColumns(); - if (props == null) + rowBuilder.Clear(); + foreach (var column in row) { - _writer.Write(_configuration.NewLine); - _writer.Flush(); - return 0; + AppendColumn(rowBuilder, column); } - - if (_printHeader) - { - _writer.Write(GetHeader(props)); - _writer.Write(_configuration.NewLine); - } - - if (writeAdapter == null) - return 0; - - var rowBuilder = new StringBuilder(); - var rowsWritten = 0; - - foreach (var row in writeAdapter.GetRows(props)) - { - rowBuilder.Clear(); - foreach (var column in row) - { - AppendColumn(rowBuilder, column); - } - RemoveTrailingSeparator(rowBuilder); - _writer.Write(rowBuilder.ToString()); - _writer.Write(_configuration.NewLine); + RemoveTrailingSeparator(rowBuilder); + _writer.Write(rowBuilder.ToString()); + _writer.Write(_configuration.NewLine); - rowsWritten++; - } - return rowsWritten; + rowsWritten++; } + return rowsWritten; + } - private async Task WriteValuesAsync(StreamWriter writer, object values, string seperator, string newLine, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + private async Task WriteValuesAsync(StreamWriter writer, object values, string seperator, string newLine, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - IMiniExcelWriteAdapter writeAdapter = null; + IMiniExcelWriteAdapter writeAdapter = null; #if NETSTANDARD2_0_OR_GREATER || NET - IAsyncMiniExcelWriteAdapter asyncWriteAdapter = null; + IAsyncMiniExcelWriteAdapter asyncWriteAdapter = null; #endif - try - { + try + { #if NETSTANDARD2_0_OR_GREATER || NET - if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out asyncWriteAdapter)) - { - writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); - } + if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out asyncWriteAdapter)) + { + writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); + } - var props = writeAdapter != null - ? writeAdapter.GetColumns() - : await asyncWriteAdapter.GetColumnsAsync(); + var props = writeAdapter != null + ? writeAdapter.GetColumns() + : await asyncWriteAdapter.GetColumnsAsync(); #else writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); var props = writeAdapter.GetColumns(); #endif - if (props == null) - { - await _writer.WriteAsync(_configuration.NewLine); - await _writer.FlushAsync(); - return 0; - } + if (props == null) + { + await _writer.WriteAsync(_configuration.NewLine); + await _writer.FlushAsync(); + return 0; + } - if (_printHeader) - { - await _writer.WriteAsync(GetHeader(props)); - await _writer.WriteAsync(newLine); - } + if (_printHeader) + { + await _writer.WriteAsync(GetHeader(props)); + await _writer.WriteAsync(newLine); + } - var rowBuilder = new StringBuilder(); - var rowsWritten = 0; + var rowBuilder = new StringBuilder(); + var rowsWritten = 0; - if (writeAdapter != null) + if (writeAdapter != null) + { + foreach (var row in writeAdapter.GetRows(props, cancellationToken)) { - foreach (var row in writeAdapter.GetRows(props, cancellationToken)) + rowBuilder.Clear(); + foreach (var column in row) { - rowBuilder.Clear(); - foreach (var column in row) - { - cancellationToken.ThrowIfCancellationRequested(); - AppendColumn(rowBuilder, column); - } - - RemoveTrailingSeparator(rowBuilder); - await _writer.WriteAsync(rowBuilder.ToString()); - await _writer.WriteAsync(newLine); - - rowsWritten++; + cancellationToken.ThrowIfCancellationRequested(); + AppendColumn(rowBuilder, column); } + + RemoveTrailingSeparator(rowBuilder); + await _writer.WriteAsync(rowBuilder.ToString()); + await _writer.WriteAsync(newLine); + + rowsWritten++; } + } #if NETSTANDARD2_0_OR_GREATER || NET - else + else + { + await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken)) { - await foreach (var row in asyncWriteAdapter.GetRowsAsync(props, cancellationToken)) + cancellationToken.ThrowIfCancellationRequested(); + rowBuilder.Clear(); + + await foreach (var column in row) { cancellationToken.ThrowIfCancellationRequested(); - rowBuilder.Clear(); - - await foreach (var column in row) - { - cancellationToken.ThrowIfCancellationRequested(); - AppendColumn(rowBuilder, column); - } + AppendColumn(rowBuilder, column); + } - RemoveTrailingSeparator(rowBuilder); - await _writer.WriteAsync(rowBuilder.ToString()); - await _writer.WriteAsync(newLine); + RemoveTrailingSeparator(rowBuilder); + await _writer.WriteAsync(rowBuilder.ToString()); + await _writer.WriteAsync(newLine); - rowsWritten++; - } + rowsWritten++; } -#endif - return rowsWritten; } - finally - { -#if NETSTANDARD2_0_OR_GREATER || NET - if (asyncWriteAdapter is IAsyncDisposable asyncDisposable) - { - await asyncDisposable.DisposeAsync().ConfigureAwait(false); - } #endif - } + return rowsWritten; } - - public async Task SaveAsAsync(CancellationToken cancellationToken = default) + finally { - cancellationToken.ThrowIfCancellationRequested(); - - var seperator = _configuration.Seperator.ToString(); - var newLine = _configuration.NewLine; - - if (_value == null) +#if NETSTANDARD2_0_OR_GREATER || NET + if (asyncWriteAdapter is IAsyncDisposable asyncDisposable) { - await _writer.WriteAsync(""); - await _writer.FlushAsync(); - return new int[0]; + await asyncDisposable.DisposeAsync().ConfigureAwait(false); } - - var rowsWritten = await WriteValuesAsync(_writer, _value, seperator, newLine, cancellationToken); - await _writer.FlushAsync(); - - return new[] { rowsWritten }; +#endif } + } + + public async Task SaveAsAsync(CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); + + var seperator = _configuration.Seperator.ToString(); + var newLine = _configuration.NewLine; - public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + if (_value == null) { - var rowsWritten = await SaveAsAsync(cancellationToken); - return rowsWritten.FirstOrDefault(); + await _writer.WriteAsync(""); + await _writer.FlushAsync(); + return new int[0]; } - public string ToCsvString(object value, ExcelColumnInfo p) - { - if (value == null) - return ""; + var rowsWritten = await WriteValuesAsync(_writer, _value, seperator, newLine, cancellationToken); + await _writer.FlushAsync(); + + return new[] { rowsWritten }; + } - if (value is DateTime dateTime) + public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + var rowsWritten = await SaveAsAsync(cancellationToken); + return rowsWritten.FirstOrDefault(); + } + + public string ToCsvString(object value, ExcelColumnInfo p) + { + if (value == null) + return ""; + + if (value is DateTime dateTime) + { + if (p?.ExcelFormat != null) { - if (p?.ExcelFormat != null) - { - return dateTime.ToString(p.ExcelFormat, _configuration.Culture); - } - return _configuration.Culture.Equals(CultureInfo.InvariantCulture) - ? dateTime.ToString("yyyy-MM-dd HH:mm:ss", _configuration.Culture) - : dateTime.ToString(_configuration.Culture); + return dateTime.ToString(p.ExcelFormat, _configuration.Culture); } + return _configuration.Culture.Equals(CultureInfo.InvariantCulture) + ? dateTime.ToString("yyyy-MM-dd HH:mm:ss", _configuration.Culture) + : dateTime.ToString(_configuration.Culture); + } - if (p?.ExcelFormat != null && value is IFormattable formattableValue) - return formattableValue.ToString(p.ExcelFormat, _configuration.Culture); + if (p?.ExcelFormat != null && value is IFormattable formattableValue) + return formattableValue.ToString(p.ExcelFormat, _configuration.Culture); - return Convert.ToString(value, _configuration.Culture); - } + return Convert.ToString(value, _configuration.Culture); + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - if (!_disposedValue) + if (disposing) { - if (disposing) - { - _writer.Dispose(); - // TODO: dispose managed state (managed objects) - } - - // TODO: free unmanaged resources (unmanaged objects) and override finalizer - // TODO: set large fields to null - _disposedValue = true; + _writer.Dispose(); + // TODO: dispose managed state (managed objects) } - } - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - ~CsvWriter() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: false); + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _disposedValue = true; } + } - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + ~CsvWriter() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/src/MiniExcel/ExcelFactory.cs b/src/MiniExcel/ExcelFactory.cs index 36760508..d48fe994 100644 --- a/src/MiniExcel/ExcelFactory.cs +++ b/src/MiniExcel/ExcelFactory.cs @@ -1,62 +1,59 @@ -namespace MiniExcelLibs -{ - using MiniExcelLibs.Csv; - using MiniExcelLibs.OpenXml; - using MiniExcelLibs.OpenXml.SaveByTemplate; - using System; - using System.IO; +using MiniExcelLibs.Csv; +using MiniExcelLibs.OpenXml; +using MiniExcelLibs.OpenXml.SaveByTemplate; + +namespace MiniExcelLibs; - internal static class ExcelReaderFactory +internal static class ExcelReaderFactory +{ + internal static IExcelReader GetProvider(Stream stream, ExcelType excelType, IConfiguration configuration) { - internal static IExcelReader GetProvider(Stream stream, ExcelType excelType, IConfiguration configuration) + switch (excelType) { - switch (excelType) - { - case ExcelType.CSV: - return new CsvReader(stream, configuration); - case ExcelType.XLSX: - return new ExcelOpenXmlSheetReader(stream, configuration); - default: - throw new NotSupportedException("Something went wrong. Please report this issue you are experiencing with MiniExcel."); - } + case ExcelType.CSV: + return new CsvReader(stream, configuration); + case ExcelType.XLSX: + return new ExcelOpenXmlSheetReader(stream, configuration); + default: + throw new NotSupportedException("Something went wrong. Please report this issue you are experiencing with MiniExcel."); } } +} - internal static class ExcelWriterFactory +internal static class ExcelWriterFactory +{ + internal static IExcelWriter GetProvider(Stream stream, object value, string sheetName, ExcelType excelType, IConfiguration configuration, bool printHeader) { - internal static IExcelWriter GetProvider(Stream stream, object value, string sheetName, ExcelType excelType, IConfiguration configuration, bool printHeader) - { - if (string.IsNullOrEmpty(sheetName)) - throw new ArgumentException("Sheet names can not be empty or null", nameof(sheetName)); - if (sheetName.Length > 31 && excelType == ExcelType.XLSX) - throw new ArgumentException("Sheet names must be less than 31 characters", nameof(sheetName)); - if (excelType == ExcelType.UNKNOWN) - throw new ArgumentException("Excel type cannot be ExcelType.UNKNOWN", nameof(excelType)); + if (string.IsNullOrEmpty(sheetName)) + throw new ArgumentException("Sheet names can not be empty or null", nameof(sheetName)); + if (sheetName.Length > 31 && excelType == ExcelType.XLSX) + throw new ArgumentException("Sheet names must be less than 31 characters", nameof(sheetName)); + if (excelType == ExcelType.UNKNOWN) + throw new ArgumentException("Excel type cannot be ExcelType.UNKNOWN", nameof(excelType)); - switch (excelType) - { - case ExcelType.CSV: - return new CsvWriter(stream, value, configuration, printHeader); - case ExcelType.XLSX: - return new ExcelOpenXmlSheetWriter(stream, value, sheetName, configuration, printHeader); - default: - throw new NotSupportedException($"The {excelType} Excel format is not supported"); - } + switch (excelType) + { + case ExcelType.CSV: + return new CsvWriter(stream, value, configuration, printHeader); + case ExcelType.XLSX: + return new ExcelOpenXmlSheetWriter(stream, value, sheetName, configuration, printHeader); + default: + throw new NotSupportedException($"The {excelType} Excel format is not supported"); } } +} - internal static class ExcelTemplateFactory +internal static class ExcelTemplateFactory +{ + internal static IExcelTemplateAsync GetProvider(Stream stream, IConfiguration configuration, ExcelType excelType = ExcelType.XLSX) { - internal static IExcelTemplateAsync GetProvider(Stream stream, IConfiguration configuration, ExcelType excelType = ExcelType.XLSX) + switch (excelType) { - switch (excelType) - { - case ExcelType.XLSX: - var valueExtractor = new InputValueExtractor(); - return new ExcelOpenXmlTemplate(stream, configuration, valueExtractor); - default: - throw new NotSupportedException("Something went wrong. Please report this issue you are experiencing with MiniExcel."); - } + case ExcelType.XLSX: + var valueExtractor = new InputValueExtractor(); + return new ExcelOpenXmlTemplate(stream, configuration, valueExtractor); + default: + throw new NotSupportedException("Something went wrong. Please report this issue you are experiencing with MiniExcel."); } } -} +} \ No newline at end of file diff --git a/src/MiniExcel/ExcelType.cs b/src/MiniExcel/ExcelType.cs index 90709b2e..4a610571 100644 --- a/src/MiniExcel/ExcelType.cs +++ b/src/MiniExcel/ExcelType.cs @@ -1,13 +1,12 @@ -namespace MiniExcelLibs +namespace MiniExcelLibs; + +public enum ExcelType { - public enum ExcelType - { - XLSX, - //XLS, - CSV, - /// - /// Will auto check excel type by stream or file path - /// - UNKNOWN - } -} + XLSX, + //XLS, + CSV, + /// + /// Will auto check excel type by stream or file path + /// + UNKNOWN +} \ No newline at end of file diff --git a/src/MiniExcel/Exceptions/ExcelColumnNotFoundException.cs b/src/MiniExcel/Exceptions/ExcelColumnNotFoundException.cs index 5bd4492a..126044f9 100644 --- a/src/MiniExcel/Exceptions/ExcelColumnNotFoundException.cs +++ b/src/MiniExcel/Exceptions/ExcelColumnNotFoundException.cs @@ -1,24 +1,21 @@ -using System.Collections.Generic; +namespace MiniExcelLibs.Exceptions; -namespace MiniExcelLibs.Exceptions +public class ExcelColumnNotFoundException : KeyNotFoundException { - public class ExcelColumnNotFoundException : KeyNotFoundException - { - public string ColumnName { get; set; } - public string[] ColumnAliases { get; } - public string ColumnIndex { get; set; } - public int RowIndex { get; set; } - public IDictionary Headers { get; } - public object RowValues { get; set; } + public string ColumnName { get; set; } + public string[] ColumnAliases { get; } + public string ColumnIndex { get; set; } + public int RowIndex { get; set; } + public IDictionary Headers { get; } + public object RowValues { get; set; } - public ExcelColumnNotFoundException(string columnIndex, string columnName, string[] columnAliases, int rowIndex, IDictionary headers, object value, string message) : base(message) - { - ColumnIndex = columnIndex; - ColumnName = columnName; - ColumnAliases = columnAliases; - RowIndex = rowIndex; - Headers = headers; - RowValues = value; - } + public ExcelColumnNotFoundException(string columnIndex, string columnName, string[] columnAliases, int rowIndex, IDictionary headers, object value, string message) : base(message) + { + ColumnIndex = columnIndex; + ColumnName = columnName; + ColumnAliases = columnAliases; + RowIndex = rowIndex; + Headers = headers; + RowValues = value; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/Exceptions/ExcelInvalidCastException.cs b/src/MiniExcel/Exceptions/ExcelInvalidCastException.cs index 35155e8b..c8ce29dc 100644 --- a/src/MiniExcel/Exceptions/ExcelInvalidCastException.cs +++ b/src/MiniExcel/Exceptions/ExcelInvalidCastException.cs @@ -1,19 +1,16 @@ -using System; +namespace MiniExcelLibs.Exceptions; -namespace MiniExcelLibs.Exceptions +public class ExcelInvalidCastException : InvalidCastException { - public class ExcelInvalidCastException : InvalidCastException + public string ColumnName { get; set; } + public int Row { get; set; } + public object Value { get; set; } + public Type InvalidCastType { get; set; } + public ExcelInvalidCastException(string columnName, int row, object value, Type invalidCastType, string message) : base(message) { - public string ColumnName { get; set; } - public int Row { get; set; } - public object Value { get; set; } - public Type InvalidCastType { get; set; } - public ExcelInvalidCastException(string columnName, int row, object value, Type invalidCastType, string message) : base(message) - { - ColumnName = columnName; - Row = row; - Value = value; - InvalidCastType = invalidCastType; - } + ColumnName = columnName; + Row = row; + Value = value; + InvalidCastType = invalidCastType; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/Exceptions/MiniExcelNotSerializableException.cs b/src/MiniExcel/Exceptions/MiniExcelNotSerializableException.cs index f86f82cf..5a953dd3 100644 --- a/src/MiniExcel/Exceptions/MiniExcelNotSerializableException.cs +++ b/src/MiniExcel/Exceptions/MiniExcelNotSerializableException.cs @@ -1,15 +1,13 @@ -using System; using System.Reflection; -namespace MiniExcelLibs.Exceptions +namespace MiniExcelLibs.Exceptions; + +public class MiniExcelNotSerializableException : InvalidOperationException { - public class MiniExcelNotSerializableException : InvalidOperationException - { - public MemberInfo Member { get; } + public MemberInfo Member { get; } - public MiniExcelNotSerializableException(string message, MemberInfo member) : base(message) - { - Member = member; - } + public MiniExcelNotSerializableException(string message, MemberInfo member) : base(message) + { + Member = member; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/IConfiguration.cs b/src/MiniExcel/IConfiguration.cs index af10a3f6..4fdc59c2 100644 --- a/src/MiniExcel/IConfiguration.cs +++ b/src/MiniExcel/IConfiguration.cs @@ -1,42 +1,41 @@ using MiniExcelLibs.Attributes; using System.Globalization; -namespace MiniExcelLibs +namespace MiniExcelLibs; + +public interface IConfiguration { } +public abstract class Configuration : IConfiguration { - public interface IConfiguration { } - public abstract class Configuration : IConfiguration - { - public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; - public DynamicExcelColumn[] DynamicColumns { get; set; } - public int BufferSize { get; set; } = 1024 * 512; - public bool FastMode { get; set; } = false; + public CultureInfo Culture { get; set; } = CultureInfo.InvariantCulture; + public DynamicExcelColumn[] DynamicColumns { get; set; } + public int BufferSize { get; set; } = 1024 * 512; + public bool FastMode { get; set; } = false; - /// - /// When exporting using DataReader, the data not in DynamicColumn will be filtered. - /// - public bool DynamicColumnFirst { get; set; } = false; + /// + /// When exporting using DataReader, the data not in DynamicColumn will be filtered. + /// + public bool DynamicColumnFirst { get; set; } = false; - /// - /// Specifies when and how DateTime values are converted to DateOnly values. - /// - public DateOnlyConversionMode DateOnlyConversionMode { get; set; } - } + /// + /// Specifies when and how DateTime values are converted to DateOnly values. + /// + public DateOnlyConversionMode DateOnlyConversionMode { get; set; } +} - public enum DateOnlyConversionMode - { - /// - /// No conversion is applied and DateOnly values are not transformed. - /// - None, +public enum DateOnlyConversionMode +{ + /// + /// No conversion is applied and DateOnly values are not transformed. + /// + None, - /// - /// Allows conversion from DateTime to DateOnly only if the time component is exactly midnight (00:00:00). - /// - RequireMidnight, + /// + /// Allows conversion from DateTime to DateOnly only if the time component is exactly midnight (00:00:00). + /// + RequireMidnight, - /// - /// Converts DateTime to DateOnly by ignoring the time part completely, assuming the time component is not critical. - /// - IgnoreTimePart - } -} + /// + /// Converts DateTime to DateOnly by ignoring the time part completely, assuming the time component is not critical. + /// + IgnoreTimePart +} \ No newline at end of file diff --git a/src/MiniExcel/IExcelReader.cs b/src/MiniExcel/IExcelReader.cs index 0581cd34..fdb0fee4 100644 --- a/src/MiniExcel/IExcelReader.cs +++ b/src/MiniExcel/IExcelReader.cs @@ -1,23 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace MiniExcelLibs; -namespace MiniExcelLibs +internal interface IExcelReader : IDisposable { - internal interface IExcelReader : IDisposable - { - IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell); - IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new(); - Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default); - Task> QueryAsync(string sheetName, string startCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); - IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell); - IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new(); - Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default); - Task> QueryRangeAsync(string sheetName, string startCell, string endCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); - IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex); - IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new(); - Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default); - Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); - } -} + IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell); + IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new(); + Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default); + Task> QueryAsync(string sheetName, string startCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); + IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell); + IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new(); + Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default); + Task> QueryRangeAsync(string sheetName, string startCell, string endCell, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); + IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex); + IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new(); + Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default); + Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader, CancellationToken cancellationToken = default) where T : class, new(); +} \ No newline at end of file diff --git a/src/MiniExcel/IExcelTemplate.cs b/src/MiniExcel/IExcelTemplate.cs index f817c12a..f8ec4337 100644 --- a/src/MiniExcel/IExcelTemplate.cs +++ b/src/MiniExcel/IExcelTemplate.cs @@ -1,24 +1,19 @@ -using System.IO; -using System.Threading; -using System.Threading.Tasks; +namespace MiniExcelLibs; -namespace MiniExcelLibs +internal interface IExcelTemplate { - internal interface IExcelTemplate - { - void SaveAsByTemplate(string templatePath, object value); - void SaveAsByTemplate(byte[] templateBtyes, object value); - void SaveAsByTemplate(Stream templateStream, object value); - void MergeSameCells(string path); - void MergeSameCells(byte[] fileInBytes); - } - - internal interface IExcelTemplateAsync : IExcelTemplate - { - Task SaveAsByTemplateAsync(string templatePath, object value, CancellationToken cancellationToken = default(CancellationToken)); - Task SaveAsByTemplateAsync(byte[] templateBtyes, object value, CancellationToken cancellationToken = default(CancellationToken)); - Task SaveAsByTemplateAsync(Stream templateStream, object value, CancellationToken cancellationToken = default(CancellationToken)); - Task MergeSameCellsAsync(string path, CancellationToken cancellationToken = default(CancellationToken)); - Task MergeSameCellsAsync(byte[] fileInBytes, CancellationToken cancellationToken = default(CancellationToken)); - } + void SaveAsByTemplate(string templatePath, object value); + void SaveAsByTemplate(byte[] templateBtyes, object value); + void SaveAsByTemplate(Stream templateStream, object value); + void MergeSameCells(string path); + void MergeSameCells(byte[] fileInBytes); } + +internal interface IExcelTemplateAsync : IExcelTemplate +{ + Task SaveAsByTemplateAsync(string templatePath, object value, CancellationToken cancellationToken = default(CancellationToken)); + Task SaveAsByTemplateAsync(byte[] templateBtyes, object value, CancellationToken cancellationToken = default(CancellationToken)); + Task SaveAsByTemplateAsync(Stream templateStream, object value, CancellationToken cancellationToken = default(CancellationToken)); + Task MergeSameCellsAsync(string path, CancellationToken cancellationToken = default(CancellationToken)); + Task MergeSameCellsAsync(byte[] fileInBytes, CancellationToken cancellationToken = default(CancellationToken)); +} \ No newline at end of file diff --git a/src/MiniExcel/IExcelWriter.cs b/src/MiniExcel/IExcelWriter.cs index feea4171..87ddfecb 100644 --- a/src/MiniExcel/IExcelWriter.cs +++ b/src/MiniExcel/IExcelWriter.cs @@ -1,13 +1,9 @@ -using System.Threading; -using System.Threading.Tasks; +namespace MiniExcelLibs; -namespace MiniExcelLibs +internal interface IExcelWriter { - internal interface IExcelWriter - { - int[] SaveAs(); - Task SaveAsAsync(CancellationToken cancellationToken = default); - int Insert(bool overwriteSheet = false); - Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default); - } -} + int[] SaveAs(); + Task SaveAsAsync(CancellationToken cancellationToken = default); + int Insert(bool overwriteSheet = false); + Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/MiniExcel/IMiniExcelDataReader.cs b/src/MiniExcel/IMiniExcelDataReader.cs index 3f304451..66d0d02e 100644 --- a/src/MiniExcel/IMiniExcelDataReader.cs +++ b/src/MiniExcel/IMiniExcelDataReader.cs @@ -1,24 +1,19 @@ -using System; -using System.Data; -using System.Threading; -using System.Threading.Tasks; +using System.Data; -namespace MiniExcelLibs -{ +namespace MiniExcelLibs; #if !NET8_0_OR_GREATER public interface IMiniExcelDataReader : IDataReader #else - public interface IMiniExcelDataReader : IDataReader, IAsyncDisposable +public interface IMiniExcelDataReader : IDataReader, IAsyncDisposable #endif - { - Task CloseAsync(); +{ + Task CloseAsync(); - Task GetNameAsync(int i, CancellationToken cancellationToken = default); + Task GetNameAsync(int i, CancellationToken cancellationToken = default); - Task GetValueAsync(int i, CancellationToken cancellationToken = default); + Task GetValueAsync(int i, CancellationToken cancellationToken = default); - Task NextResultAsync(CancellationToken cancellationToken = default); + Task NextResultAsync(CancellationToken cancellationToken = default); - Task ReadAsync(CancellationToken cancellationToken = default); - } -} + Task ReadAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/MiniExcel/MiniExcel.Async.cs b/src/MiniExcel/MiniExcel.Async.cs index 0fff8d9c..96eb40c5 100644 --- a/src/MiniExcel/MiniExcel.Async.cs +++ b/src/MiniExcel/MiniExcel.Async.cs @@ -1,170 +1,163 @@ -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's Insert does not support the .xlsm format"); +using MiniExcelLibs.OpenXml; +using System.Collections; +using System.Data; +using MiniExcelLibs.Utils; - if (!File.Exists(path)) - { - var rowsWritten = await SaveAsAsync(path, value, printHeader, sheetName, excelType, configuration, cancellationToken: cancellationToken); - return rowsWritten.FirstOrDefault(); - } +namespace MiniExcelLibs; - if (excelType == ExcelType.CSV) - { - using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) - return 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)) - return await InsertAsync(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet, cancellationToken); - } - } +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's Insert does not support the .xlsm format"); - public static async TaskInsertAsync(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false, CancellationToken cancellationToken = default) + if (!File.Exists(path)) { - stream.Seek(0, SeekOrigin.End); - // reuse code - if (excelType == ExcelType.CSV) - { - var newValue = value is IEnumerable || value is IDataReader ? value : new[]{value}.AsEnumerable(); - return await ExcelWriterFactory.GetProvider(stream, newValue, sheetName, excelType, configuration, false).InsertAsync(overwriteSheet, cancellationToken); - } - else - { - var configOrDefault = configuration ?? new OpenXmlConfiguration { FastMode = true }; - return await ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configOrDefault, printHeader).InsertAsync(overwriteSheet, cancellationToken); - } + var rowsWritten = await SaveAsAsync(path, value, printHeader, sheetName, excelType, configuration, cancellationToken: cancellationToken); + return rowsWritten.FirstOrDefault(); } - 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) + if (excelType == ExcelType.CSV) { - if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") - throw new NotSupportedException("MiniExcel's SaveAs does not support the .xlsm format"); - - using (var stream = overwriteFile ? File.Create(path) : new FileStream(path, FileMode.CreateNew)) - return await SaveAsAsync(stream, value, printHeader, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, cancellationToken); + using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) + return await InsertAsync(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet, cancellationToken); } - - public static async Task SaveAsAsync(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) + else { - return await ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).SaveAsAsync(cancellationToken); + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.SequentialScan)) + return await InsertAsync(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet, cancellationToken); } + } - public static async Task MergeSameCellsAsync(string mergedFilePath, string path, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, CancellationToken cancellationToken = default) + public static async TaskInsertAsync(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) { - await Task.Run(() => MergeSameCells(mergedFilePath, path, excelType, configuration), cancellationToken).ConfigureAwait(false); + var newValue = value is IEnumerable || value is IDataReader ? value : new[]{value}.AsEnumerable(); + return await ExcelWriterFactory.GetProvider(stream, newValue, sheetName, excelType, configuration, false).InsertAsync(overwriteSheet, cancellationToken); } - - public static async Task MergeSameCellsAsync(this Stream stream, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) + else { - await ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCellsAsync(path, cancellationToken); + var configOrDefault = configuration ?? new OpenXmlConfiguration { FastMode = true }; + return await ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configOrDefault, printHeader).InsertAsync(overwriteSheet, cancellationToken); } + } - public static async Task MergeSameCellsAsync(this Stream stream, byte[] fileBytes, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCellsAsync(fileBytes, cancellationToken); - } + 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) + { + if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") + throw new NotSupportedException("MiniExcel's SaveAs does not support the .xlsm format"); - public static async Task> QueryAsync(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - return await Task.Run(() => Query(path, useHeaderRow, sheetName, excelType, startCell, configuration), cancellationToken); - } + using (var stream = overwriteFile ? File.Create(path) : new FileStream(path, FileMode.CreateNew)) + return await SaveAsAsync(stream, value, printHeader, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, cancellationToken); + } - public static async Task> QueryAsync(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default, bool hasHeader = true) where T : class, new() - { - return await ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration).QueryAsync(sheetName, startCell, hasHeader, cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsAsync(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + return await ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).SaveAsAsync(cancellationToken); + } + + public static async Task MergeSameCellsAsync(string mergedFilePath, string path, ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await Task.Run(() => MergeSameCells(mergedFilePath, path, excelType, configuration), cancellationToken).ConfigureAwait(false); + } + + public static async Task MergeSameCellsAsync(this Stream stream, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCellsAsync(path, cancellationToken); + } + + public static async Task MergeSameCellsAsync(this Stream stream, byte[] fileBytes, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCellsAsync(fileBytes, cancellationToken); + } + + public static async Task> QueryAsync(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + return await Task.Run(() => Query(path, useHeaderRow, sheetName, excelType, startCell, configuration), cancellationToken); + } + + public static async Task> QueryAsync(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default, bool hasHeader = true) where T : class, new() + { + return await ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration).QueryAsync(sheetName, startCell, hasHeader, cancellationToken).ConfigureAwait(false); + } + + public static async Task> QueryAsync(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default, bool hasHeader = true) where T : class, new() + { + return await Task.Run(() => Query(path, sheetName, excelType, startCell, configuration, hasHeader), cancellationToken).ConfigureAwait(false); + } - public static async Task> QueryAsync(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default, bool hasHeader = true) where T : class, new() + public static async Task> QueryAsync(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + var tcs = new TaskCompletionSource>(); + cancellationToken.Register(() => { - return await Task.Run(() => Query(path, sheetName, excelType, startCell, configuration, hasHeader), cancellationToken).ConfigureAwait(false); - } + tcs.TrySetCanceled(); + }); - public static async Task> QueryAsync(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) + await Task.Run(() => { - var tcs = new TaskCompletionSource>(); - cancellationToken.Register(() => + try { - tcs.TrySetCanceled(); - }); - - await Task.Run(() => + tcs.TrySetResult(Query(stream, useHeaderRow, sheetName, excelType, startCell, configuration)); + } + catch (Exception ex) { - try - { - tcs.TrySetResult(Query(stream, useHeaderRow, sheetName, excelType, startCell, configuration)); - } - catch (Exception ex) - { - tcs.TrySetException(ex); - } - }, cancellationToken); - - return await tcs.Task; - } + tcs.TrySetException(ex); + } + }, cancellationToken); + + return await tcs.Task; + } - public static async Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templatePath, value, cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(this Stream stream, string templatePath, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templatePath, value, cancellationToken).ConfigureAwait(false); + } - public static async Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templateBytes, value, cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(this Stream stream, byte[] templateBytes, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templateBytes, value, cancellationToken).ConfigureAwait(false); + } - public static async Task SaveAsByTemplateAsync(this Stream stream, Stream templateStream, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templateStream, value, cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(this Stream stream, Stream templateStream, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplateAsync(templateStream, value, cancellationToken).ConfigureAwait(false); + } - public static async Task SaveAsByTemplateAsync(string path, string templatePath, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await Task.Run(() => SaveAsByTemplate(path, templatePath, value, configuration), cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(string path, string templatePath, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await Task.Run(() => SaveAsByTemplate(path, templatePath, value, configuration), cancellationToken).ConfigureAwait(false); + } - public static async Task SaveAsByTemplateAsync(string path, Stream templateStream, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await Task.Run(() => SaveAsByTemplate(path, templateStream, value, configuration), cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(string path, Stream templateStream, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await Task.Run(() => SaveAsByTemplate(path, templateStream, value, configuration), cancellationToken).ConfigureAwait(false); + } - public static async Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - await Task.Run(() => SaveAsByTemplate(path, templateBytes, value, configuration), cancellationToken).ConfigureAwait(false); - } + public static async Task SaveAsByTemplateAsync(string path, byte[] templateBytes, object value, IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + await Task.Run(() => SaveAsByTemplate(path, templateBytes, value, configuration), cancellationToken).ConfigureAwait(false); + } - /// - /// QueryAsDataTable is not recommended, because it'll load all data into memory. - /// - [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] - public static async Task QueryAsDataTableAsync(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - return await Task.Run(() => QueryAsDataTable(path, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration), cancellationToken).ConfigureAwait(false); - } + /// + /// QueryAsDataTable is not recommended, because it'll load all data into memory. + /// + [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] + public static async Task QueryAsDataTableAsync(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + return await Task.Run(() => QueryAsDataTable(path, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration), cancellationToken).ConfigureAwait(false); + } - /// - /// QueryAsDataTable is not recommended, because it'll load all data into memory. - /// - [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] - public static async Task QueryAsDataTableAsync(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) - { - return await Task.Run(() => QueryAsDataTable(stream, useHeaderRow, sheetName, excelType, startCell, configuration), cancellationToken).ConfigureAwait(false); - } + /// + /// QueryAsDataTable is not recommended, because it'll load all data into memory. + /// + [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] + public static async Task QueryAsDataTableAsync(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, CancellationToken cancellationToken = default) + { + return await Task.Run(() => QueryAsDataTable(stream, useHeaderRow, sheetName, excelType, startCell, configuration), cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/MiniExcel.Code.cs b/src/MiniExcel/MiniExcel.Code.cs deleted file mode 100644 index 0df1e936..00000000 --- a/src/MiniExcel/MiniExcel.Code.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace MiniExcelLibs -{ - public static partial class MiniExcel - { - public static string LISENCE_CODE = null; - } -} diff --git a/src/MiniExcel/MiniExcel.cs b/src/MiniExcel/MiniExcel.cs index 22f500e8..6ee3ea3f 100644 --- a/src/MiniExcel/MiniExcel.cs +++ b/src/MiniExcel/MiniExcel.cs @@ -1,358 +1,353 @@ -using System; -using System.Collections; -using System.Collections.Generic; +using System.Collections; using System.Data; using System.Dynamic; -using System.IO; -using System.Linq; using MiniExcelLibs.OpenXml; using MiniExcelLibs.OpenXml.Models; using MiniExcelLibs.Picture; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; -namespace MiniExcelLibs +namespace MiniExcelLibs; + +public static partial class MiniExcel { - public static partial class MiniExcel + public static void AddPicture(string path, params MiniExcelPicture[] images) { - public static void AddPicture(string path, params MiniExcelPicture[] images) - { - using (var stream = File.Open(path, FileMode.OpenOrCreate)) - MiniExcelPictureImplement.AddPicture(stream, images); - } + using (var stream = File.Open(path, FileMode.OpenOrCreate)) + MiniExcelPictureImplement.AddPicture(stream, images); + } + + public static void AddPicture(Stream excelStream, params MiniExcelPicture[] images) + { + MiniExcelPictureImplement.AddPicture(excelStream, images); + } + + public static MiniExcelDataReader GetReader(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + var stream = FileHelper.OpenSharedRead(path); + return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); + } + + public static MiniExcelDataReader GetReader(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); + } + + public static int 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() == ".xlsm") + throw new NotSupportedException("MiniExcel's Insert does not support the .xlsm format"); - public static void AddPicture(Stream excelStream, params MiniExcelPicture[] images) + if (!File.Exists(path)) { - MiniExcelPictureImplement.AddPicture(excelStream, images); + var rowsWritten = SaveAs(path, value, printHeader, sheetName, excelType, configuration); + return rowsWritten.FirstOrDefault(); } - public static MiniExcelDataReader GetReader(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + if (excelType == ExcelType.CSV) { - var stream = FileHelper.OpenSharedRead(path); - return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); + using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) + return Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet); } - - public static MiniExcelDataReader GetReader(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + else { - return new MiniExcelDataReader(stream, useHeaderRow, sheetName, excelType, startCell, configuration); + using (var stream = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096, FileOptions.SequentialScan)) + return Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet); } + } - public static int Insert(string path, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false) + public static int Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false) + { + stream.Seek(0, SeekOrigin.End); + if (excelType == ExcelType.CSV) { - if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") - throw new NotSupportedException("MiniExcel's Insert does not support the .xlsm format"); - - if (!File.Exists(path)) - { - var rowsWritten = SaveAs(path, value, printHeader, sheetName, excelType, configuration); - return rowsWritten.FirstOrDefault(); - } - - if (excelType == ExcelType.CSV) - { - using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read, 4096, FileOptions.SequentialScan)) - return 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)) - return Insert(stream, value, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration, printHeader, overwriteSheet); - } + var newValue = value is IEnumerable || value is IDataReader ? value : new[] { value }; + return ExcelWriterFactory.GetProvider(stream, newValue, sheetName, excelType, configuration, false).Insert(overwriteSheet); } - - public static int Insert(this Stream stream, object value, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null, bool printHeader = true, bool overwriteSheet = false) + else { - stream.Seek(0, SeekOrigin.End); - if (excelType == ExcelType.CSV) - { - var newValue = value is IEnumerable || value is IDataReader ? value : new[] { value }; - return ExcelWriterFactory.GetProvider(stream, newValue, sheetName, excelType, configuration, false).Insert(overwriteSheet); - } - else - { - var configOrDefault = configuration ?? new OpenXmlConfiguration { FastMode = true }; - return ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configOrDefault, printHeader).Insert(overwriteSheet); - } + var configOrDefault = configuration ?? new OpenXmlConfiguration { FastMode = true }; + return ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configOrDefault, printHeader).Insert(overwriteSheet); } + } - public static int[] SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool overwriteFile = false) - { - if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") - throw new NotSupportedException("MiniExcel's SaveAs does not support the .xlsm format"); + public static int[] SaveAs(string path, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.UNKNOWN, IConfiguration configuration = null, bool overwriteFile = false) + { + if (Path.GetExtension(path).ToLowerInvariant() == ".xlsm") + throw new NotSupportedException("MiniExcel's SaveAs does not support the .xlsm format"); - using (var stream = overwriteFile ? File.Create(path) : new FileStream(path, FileMode.CreateNew)) - return SaveAs(stream, value, printHeader, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration); - } + using (var stream = overwriteFile ? File.Create(path) : new FileStream(path, FileMode.CreateNew)) + return SaveAs(stream, value, printHeader, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), configuration); + } - public static int[] SaveAs(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) - { - return ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).SaveAs(); - } + public static int[] SaveAs(this Stream stream, object value, bool printHeader = true, string sheetName = "Sheet1", ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + return ExcelWriterFactory.GetProvider(stream, value, sheetName, excelType, configuration, printHeader).SaveAs(); + } - public static IEnumerable Query(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, bool hasHeader = true) where T : class, new() - { - using (var stream = FileHelper.OpenSharedRead(path)) - foreach (var item in Query(stream, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration, hasHeader)) - yield return item; //Foreach yield return twice reason : https://stackoverflow.com/questions/66791982/ienumerable-extract-code-lazy-loading-show-stream-was-not-readable - } + public static IEnumerable Query(string path, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, bool hasHeader = true) where T : class, new() + { + using (var stream = FileHelper.OpenSharedRead(path)) + foreach (var item in Query(stream, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration, hasHeader)) + yield return item; //Foreach yield return twice reason : https://stackoverflow.com/questions/66791982/ienumerable-extract-code-lazy-loading-show-stream-was-not-readable + } - public static IEnumerable Query(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, bool hasHeader = true) where T : class, new() - { - using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) - foreach (var item in excelReader.Query(sheetName, startCell, hasHeader)) - yield return item; - } + public static IEnumerable Query(this Stream stream, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null, bool hasHeader = true) where T : class, new() + { + using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) + foreach (var item in excelReader.Query(sheetName, startCell, hasHeader)) + yield return item; + } - public static IEnumerable Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - foreach (var item in Query(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration)) - yield return item; - } + public static IEnumerable Query(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + foreach (var item in Query(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration)) + yield return item; + } - public static IEnumerable Query(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) - { - using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) - foreach (var item in excelReader.Query(useHeaderRow, sheetName, startCell)) - yield return item.Aggregate(new ExpandoObject() as IDictionary, - (dict, p) => { dict.Add(p); return dict; }); - } + public static IEnumerable Query(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) + foreach (var item in excelReader.Query(useHeaderRow, sheetName, startCell)) + yield return item.Aggregate(new ExpandoObject() as IDictionary, + (dict, p) => { dict.Add(p); return dict; }); + } - #region QueryRange - - /// - /// Extract the given range。 Only uppercase letters are effective。 - /// e.g. - /// MiniExcel.QueryRange(path, startCell: "A2", endCell: "C3") - /// A2 represents the second row of column A, C3 represents the third row of column C - /// If you don't want to restrict rows, just don't include numbers - /// - /// - /// - /// - /// - /// top left corner - /// lower right corner - /// - /// - public static IEnumerable QueryRange(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", string endCell = "", IConfiguration configuration = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - foreach (var item in QueryRange(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, endCell, configuration)) - yield return item; - } + #region QueryRange + + /// + /// Extract the given range。 Only uppercase letters are effective。 + /// e.g. + /// MiniExcel.QueryRange(path, startCell: "A2", endCell: "C3") + /// A2 represents the second row of column A, C3 represents the third row of column C + /// If you don't want to restrict rows, just don't include numbers + /// + /// + /// + /// + /// + /// top left corner + /// lower right corner + /// + /// + public static IEnumerable QueryRange(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", string endCell = "", IConfiguration configuration = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + foreach (var item in QueryRange(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startCell, endCell, configuration)) + yield return item; + } - public static IEnumerable QueryRange(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", string endCell = "", IConfiguration configuration = null) - { - using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) - foreach (var item in excelReader.QueryRange(useHeaderRow, sheetName, startCell, endCell)) - yield return item.Aggregate(new ExpandoObject() as IDictionary, - (dict, p) => { dict.Add(p); return dict; }); - } + public static IEnumerable QueryRange(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", string endCell = "", IConfiguration configuration = null) + { + using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) + foreach (var item in excelReader.QueryRange(useHeaderRow, sheetName, startCell, endCell)) + yield return item.Aggregate(new ExpandoObject() as IDictionary, + (dict, p) => { dict.Add(p); return dict; }); + } - public static IEnumerable QueryRange(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, int startRowIndex = 1, int startColumnIndex = 1, int? endRowIndex = null, int? endColumnIndex = null, IConfiguration configuration = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - foreach (var item in QueryRange(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, configuration)) - yield return item; - } + public static IEnumerable QueryRange(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, int startRowIndex = 1, int startColumnIndex = 1, int? endRowIndex = null, int? endColumnIndex = null, IConfiguration configuration = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + foreach (var item in QueryRange(stream, useHeaderRow, sheetName, ExcelTypeHelper.GetExcelType(path, excelType), startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, configuration)) + yield return item; + } - public static IEnumerable QueryRange(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, int startRowIndex = 1, int startColumnIndex = 1, int? endRowIndex = null, int? endColumnIndex = null, IConfiguration configuration = null) - { - using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) - foreach (var item in excelReader.QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex)) - yield return item.Aggregate(new ExpandoObject() as IDictionary, - (dict, p) => { dict.Add(p); return dict; }); - } + public static IEnumerable QueryRange(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, int startRowIndex = 1, int startColumnIndex = 1, int? endRowIndex = null, int? endColumnIndex = null, IConfiguration configuration = null) + { + using (var excelReader = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration)) + foreach (var item in excelReader.QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex)) + yield return item.Aggregate(new ExpandoObject() as IDictionary, + (dict, p) => { dict.Add(p); return dict; }); + } - #endregion QueryRange + #endregion QueryRange - public static void SaveAsByTemplate(string path, string templatePath, object value, IConfiguration configuration = null) - { - using (var stream = File.Create(path)) - SaveAsByTemplate(stream, templatePath, value, configuration); - } + public static void SaveAsByTemplate(string path, string templatePath, object value, IConfiguration configuration = null) + { + using (var stream = File.Create(path)) + SaveAsByTemplate(stream, templatePath, value, configuration); + } - public static void SaveAsByTemplate(string path, Stream templateStream, object value, IConfiguration configuration = null) - { - using (var stream = File.Create(path)) - SaveAsByTemplate(stream, templateStream, value, configuration); - } + public static void SaveAsByTemplate(string path, Stream templateStream, object value, IConfiguration configuration = null) + { + using (var stream = File.Create(path)) + SaveAsByTemplate(stream, templateStream, value, configuration); + } - public static void SaveAsByTemplate(string path, byte[] templateBytes, object value, IConfiguration configuration = null) - { - using (var stream = File.Create(path)) - SaveAsByTemplate(stream, templateBytes, value, configuration); - } + public static void SaveAsByTemplate(string path, byte[] templateBytes, object value, IConfiguration configuration = null) + { + using (var stream = File.Create(path)) + SaveAsByTemplate(stream, templateBytes, value, configuration); + } - public static void SaveAsByTemplate(this Stream stream, string templatePath, object value, IConfiguration configuration = null) - { - ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templatePath, value); - } + public static void SaveAsByTemplate(this Stream stream, string templatePath, object value, IConfiguration configuration = null) + { + ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templatePath, value); + } - public static void SaveAsByTemplate(this Stream stream, Stream templateStream, object value, IConfiguration configuration = null) - { - ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templateStream, value); - } + public static void SaveAsByTemplate(this Stream stream, Stream templateStream, object value, IConfiguration configuration = null) + { + ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templateStream, value); + } - public static void SaveAsByTemplate(this Stream stream, byte[] templateBytes, object value, IConfiguration configuration = null) - { - ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templateBytes, value); - } + public static void SaveAsByTemplate(this Stream stream, byte[] templateBytes, object value, IConfiguration configuration = null) + { + ExcelTemplateFactory.GetProvider(stream, configuration).SaveAsByTemplate(templateBytes, value); + } - #region MergeCells + #region MergeCells - public static void MergeSameCells(string mergedFilePath, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) - { - using (var stream = File.Create(mergedFilePath)) - MergeSameCells(stream, path, excelType, configuration); - } + public static void MergeSameCells(string mergedFilePath, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + using (var stream = File.Create(mergedFilePath)) + MergeSameCells(stream, path, excelType, configuration); + } - public static void MergeSameCells(this Stream stream, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) - { - ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCells(path); - } + public static void MergeSameCells(this Stream stream, string path, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCells(path); + } - public static void MergeSameCells(this Stream stream, byte[] filePath, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) - { - ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCells(filePath); - } + public static void MergeSameCells(this Stream stream, byte[] filePath, ExcelType excelType = ExcelType.XLSX, IConfiguration configuration = null) + { + ExcelTemplateFactory.GetProvider(stream, configuration, excelType).MergeSameCells(filePath); + } - #endregion + #endregion - /// - /// QueryAsDataTable is not recommended, because it'll load all data into memory. - /// - [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] - public static DataTable QueryAsDataTable(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + /// + /// QueryAsDataTable is not recommended, because it'll load all data into memory. + /// + [Obsolete("QueryAsDataTable is not recommended, because it'll load all data into memory.")] + public static DataTable QueryAsDataTable(string path, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) { - using (var stream = FileHelper.OpenSharedRead(path)) - { - return QueryAsDataTable(stream, useHeaderRow, sheetName, excelType: ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration); - } + return QueryAsDataTable(stream, useHeaderRow, sheetName, excelType: ExcelTypeHelper.GetExcelType(path, excelType), startCell, configuration); } + } - public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) - { - if (sheetName == null && excelType != ExcelType.CSV) /*Issue #279*/ - sheetName = stream.GetSheetNames(configuration as OpenXmlConfiguration).First(); + public static DataTable QueryAsDataTable(this Stream stream, bool useHeaderRow = true, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + if (sheetName == null && excelType != ExcelType.CSV) /*Issue #279*/ + sheetName = stream.GetSheetNames(configuration as OpenXmlConfiguration).First(); - var dt = new DataTable(sheetName); - var first = true; - var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration).Query(false, sheetName, startCell); + var dt = new DataTable(sheetName); + var first = true; + var rows = ExcelReaderFactory.GetProvider(stream, ExcelTypeHelper.GetExcelType(stream, excelType), configuration).Query(false, sheetName, startCell); - var columnDict = new Dictionary(); - foreach (IDictionary row in rows) + var columnDict = new Dictionary(); + foreach (IDictionary row in rows) + { + if (first) { - if (first) + foreach (var entry in row) { - foreach (var entry in row) - { - var columnName = useHeaderRow ? entry.Value?.ToString() : entry.Key; - if (!string.IsNullOrWhiteSpace(columnName)) // avoid #298 : Column '' does not belong to table - { - var column = new DataColumn(columnName, typeof(object)) { Caption = columnName }; - dt.Columns.Add(column); - columnDict.Add(entry.Key, columnName);//same column name throw exception??? - } - } - dt.BeginLoadData(); - first = false; - if (useHeaderRow) + var columnName = useHeaderRow ? entry.Value?.ToString() : entry.Key; + if (!string.IsNullOrWhiteSpace(columnName)) // avoid #298 : Column '' does not belong to table { - continue; + var column = new DataColumn(columnName, typeof(object)) { Caption = columnName }; + dt.Columns.Add(column); + columnDict.Add(entry.Key, columnName);//same column name throw exception??? } } - - var newRow = dt.NewRow(); - foreach (var entry in columnDict) + dt.BeginLoadData(); + first = false; + if (useHeaderRow) { - newRow[entry.Value] = row[entry.Key]; //TODO: optimize not using string key + continue; } + } - dt.Rows.Add(newRow); + var newRow = dt.NewRow(); + foreach (var entry in columnDict) + { + newRow[entry.Value] = row[entry.Key]; //TODO: optimize not using string key } - dt.EndLoadData(); - return dt; + dt.Rows.Add(newRow); } - public static List GetSheetNames(string path, OpenXmlConfiguration config = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - return GetSheetNames(stream, config); - } + dt.EndLoadData(); + return dt; + } - public static List GetSheetNames(this Stream stream, OpenXmlConfiguration config = null) - { - config = config ?? OpenXmlConfiguration.DefaultConfig; + public static List GetSheetNames(string path, OpenXmlConfiguration config = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + return GetSheetNames(stream, config); + } - var archive = new ExcelOpenXmlZip(stream); - return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select(s => s.Name).ToList(); - } + public static List GetSheetNames(this Stream stream, OpenXmlConfiguration config = null) + { + config = config ?? OpenXmlConfiguration.DefaultConfig; - public static List GetSheetInformations(string path, OpenXmlConfiguration config = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - return GetSheetInformations(stream, config); - } + var archive = new ExcelOpenXmlZip(stream); + return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select(s => s.Name).ToList(); + } - public static List GetSheetInformations(this Stream stream, OpenXmlConfiguration config = null) - { - config = config ?? OpenXmlConfiguration.DefaultConfig; + public static List GetSheetInformations(string path, OpenXmlConfiguration config = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + return GetSheetInformations(stream, config); + } - var archive = new ExcelOpenXmlZip(stream); - return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select((s, i) => s.ToSheetInfo((uint)i)).ToList(); - } + public static List GetSheetInformations(this Stream stream, OpenXmlConfiguration config = null) + { + config = config ?? OpenXmlConfiguration.DefaultConfig; - public static ICollection GetColumns(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) - { - using (var stream = FileHelper.OpenSharedRead(path)) - return GetColumns(stream, useHeaderRow, sheetName, excelType, startCell, configuration); - } + var archive = new ExcelOpenXmlZip(stream); + return new ExcelOpenXmlSheetReader(stream, config).GetWorkbookRels(archive.entries).Select((s, i) => s.ToSheetInfo((uint)i)).ToList(); + } - public static ICollection GetColumns(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) - { - return (Query(stream, useHeaderRow, sheetName, excelType, startCell, configuration).FirstOrDefault() as IDictionary)?.Keys; - } + public static ICollection GetColumns(string path, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + using (var stream = FileHelper.OpenSharedRead(path)) + return GetColumns(stream, useHeaderRow, sheetName, excelType, startCell, configuration); + } - public static IList GetSheetDimensions(string path) - { - using (var stream = FileHelper.OpenSharedRead(path)) - return GetSheetDimensions(stream); - } + public static ICollection GetColumns(this Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + { + return (Query(stream, useHeaderRow, sheetName, excelType, startCell, configuration).FirstOrDefault() as IDictionary)?.Keys; + } - public static IList GetSheetDimensions(this Stream stream) - { - return new ExcelOpenXmlSheetReader(stream, null).GetDimensions(); - } + public static IList GetSheetDimensions(string path) + { + using (var stream = FileHelper.OpenSharedRead(path)) + return GetSheetDimensions(stream); + } - public static void ConvertCsvToXlsx(string csv, string xlsx) - { - using (var csvStream = FileHelper.OpenSharedRead(csv)) - using (var xlsxStream = new FileStream(xlsx, FileMode.CreateNew)) - { - ConvertCsvToXlsx(csvStream, xlsxStream); - } - } + public static IList GetSheetDimensions(this Stream stream) + { + return new ExcelOpenXmlSheetReader(stream, null).GetDimensions(); + } - public static void ConvertCsvToXlsx(Stream csv, Stream xlsx) + public static void ConvertCsvToXlsx(string csv, string xlsx) + { + using (var csvStream = FileHelper.OpenSharedRead(csv)) + using (var xlsxStream = new FileStream(xlsx, FileMode.CreateNew)) { - var value = Query(csv, useHeaderRow: false, excelType: ExcelType.CSV); - SaveAs(xlsx, value, printHeader: false, excelType: ExcelType.XLSX); + ConvertCsvToXlsx(csvStream, xlsxStream); } + } - public static void ConvertXlsxToCsv(string xlsx, string csv) - { - using (var xlsxStream = FileHelper.OpenSharedRead(xlsx)) - using (var csvStream = new FileStream(csv, FileMode.CreateNew)) - ConvertXlsxToCsv(xlsxStream, csvStream); - } + public static void ConvertCsvToXlsx(Stream csv, Stream xlsx) + { + var value = Query(csv, useHeaderRow: false, excelType: ExcelType.CSV); + SaveAs(xlsx, value, printHeader: false, excelType: ExcelType.XLSX); + } - public static void ConvertXlsxToCsv(Stream xlsx, Stream csv) - { - var value = Query(xlsx, useHeaderRow: false, excelType: ExcelType.XLSX); - SaveAs(csv, value, printHeader: false, excelType: ExcelType.CSV); - } + public static void ConvertXlsxToCsv(string xlsx, string csv) + { + using (var xlsxStream = FileHelper.OpenSharedRead(xlsx)) + using (var csvStream = new FileStream(csv, FileMode.CreateNew)) + ConvertXlsxToCsv(xlsxStream, csvStream); + } + + public static void ConvertXlsxToCsv(Stream xlsx, Stream csv) + { + var value = Query(xlsx, useHeaderRow: false, excelType: ExcelType.XLSX); + SaveAs(csv, value, printHeader: false, excelType: ExcelType.CSV); } } \ No newline at end of file diff --git a/src/MiniExcel/MiniExcelDataReader.cs b/src/MiniExcel/MiniExcelDataReader.cs index 6a130444..38e14198 100644 --- a/src/MiniExcel/MiniExcelDataReader.cs +++ b/src/MiniExcel/MiniExcelDataReader.cs @@ -1,99 +1,92 @@ -namespace MiniExcelLibs +namespace MiniExcelLibs; + +public class MiniExcelDataReader : MiniExcelDataReaderBase { - using System; - using System.Collections.Generic; - using System.Data; - using System.IO; - using System.Linq; + private readonly IEnumerator> _source; + private readonly int _fieldCount; + private readonly List _keys; + private readonly Stream _stream; + private bool _isFirst = true; + private bool _disposed = false; - public class MiniExcelDataReader : MiniExcelDataReaderBase + /// + /// Initializes a new instance of the class. + /// + /// The stream to read from. + /// Whether to use the header row. + /// The name of the sheet. + /// The type of the Excel file. + /// The start cell. + /// The configuration. + internal MiniExcelDataReader(Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) { - private readonly IEnumerator> _source; - private readonly int _fieldCount; - private readonly List _keys; - private readonly Stream _stream; - private bool _isFirst = true; - private bool _disposed = false; - - /// - /// Initializes a new instance of the class. - /// - /// The stream to read from. - /// Whether to use the header row. - /// The name of the sheet. - /// The type of the Excel file. - /// The start cell. - /// The configuration. - internal MiniExcelDataReader(Stream stream, bool useHeaderRow = false, string sheetName = null, ExcelType excelType = ExcelType.UNKNOWN, string startCell = "A1", IConfiguration configuration = null) + _stream = stream ?? throw new ArgumentNullException(nameof(stream)); + _source = MiniExcel.Query(_stream, useHeaderRow, sheetName, excelType, startCell, configuration).Cast>().GetEnumerator(); + if (_source.MoveNext()) { - _stream = stream ?? throw new ArgumentNullException(nameof(stream)); - _source = MiniExcel.Query(_stream, useHeaderRow, sheetName, excelType, startCell, configuration).Cast>().GetEnumerator(); - if (_source.MoveNext()) - { - _keys = _source.Current?.Keys.ToList() ?? new List(); - _fieldCount = _keys.Count; - } + _keys = _source.Current?.Keys.ToList() ?? new List(); + _fieldCount = _keys.Count; } + } - /// - public override object GetValue(int i) - { - if (_source.Current == null) - throw new InvalidOperationException("No current row available."); - return _source.Current[_keys[i]]; - } + /// + public override object GetValue(int i) + { + if (_source.Current == null) + throw new InvalidOperationException("No current row available."); + return _source.Current[_keys[i]]; + } - /// - public override int FieldCount => _fieldCount; + /// + public override int FieldCount => _fieldCount; - /// - /// - /// - /// - public override bool Read() + /// + /// + /// + /// + public override bool Read() + { + if (_isFirst) { - if (_isFirst) - { - _isFirst = false; - return true; - } - return _source.MoveNext(); + _isFirst = false; + return true; } + return _source.MoveNext(); + } - /// - public override string GetName(int i) - { - return _keys[i]; - } + /// + public override string GetName(int i) + { + return _keys[i]; + } - /// - public override int GetOrdinal(string name) - { - return _keys.IndexOf(name); - } + /// + public override int GetOrdinal(string name) + { + return _keys.IndexOf(name); + } - /// - protected override void Dispose(bool disposing) + /// + protected override void Dispose(bool disposing) + { + if (!_disposed) { - if (!_disposed) + if (disposing) { - if (disposing) - { - _stream?.Dispose(); - _source?.Dispose(); - } - _disposed = true; + _stream?.Dispose(); + _source?.Dispose(); } - base.Dispose(disposing); + _disposed = true; } + base.Dispose(disposing); + } - /// - /// Disposes the object. - /// - public new void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + /// + /// Disposes the object. + /// + public new void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/MiniExcelDataReaderBase.cs b/src/MiniExcel/MiniExcelDataReaderBase.cs index 402e96c0..f694afc8 100644 --- a/src/MiniExcel/MiniExcelDataReaderBase.cs +++ b/src/MiniExcel/MiniExcelDataReaderBase.cs @@ -1,384 +1,380 @@ -namespace MiniExcelLibs +using System.Data; + +namespace MiniExcelLibs; + +/// +/// IMiniExcelDataReader Base Class +/// +public abstract class MiniExcelDataReaderBase : IMiniExcelDataReader { - using System; - using System.Data; - using System.Threading; - using System.Threading.Tasks; + /// + /// + /// + /// + /// + public virtual object this[int i] => null; + + /// + /// + /// + /// + /// + public virtual object this[string name] => null; + + /// + /// + /// + public virtual int Depth { get; } = 0; + + /// + /// + /// + public virtual bool IsClosed { get; } = false; + + /// + /// + /// + public virtual int RecordsAffected { get; } = 0; + + /// + /// + /// + public virtual int FieldCount { get; } + + /// + /// + /// + /// + /// + public virtual bool GetBoolean(int i) => false; + + /// + /// + /// + /// + /// + public virtual byte GetByte(int i) => byte.MinValue; + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length) => 0; + + /// + /// + /// + /// + /// + public virtual char GetChar(int i) => char.MinValue; + + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length) => 0; + + /// + /// + /// + /// + /// + public virtual IDataReader GetData(int i) => null; + + /// + /// + /// + /// + /// + public virtual string GetDataTypeName(int i) => string.Empty; + + /// + /// + /// + /// + /// + public virtual DateTime GetDateTime(int i) => DateTime.MinValue; + + /// + /// + /// + /// + /// + public virtual decimal GetDecimal(int i) => 0; + + /// + /// + /// + /// + /// + public virtual double GetDouble(int i) => 0; + + /// + /// + /// + /// + /// + public virtual Type GetFieldType(int i) => null; + + /// + /// + /// + /// + /// + public virtual float GetFloat(int i) => 0f; + + /// + /// + /// + /// + /// + public virtual Guid GetGuid(int i) => Guid.Empty; + + /// + /// + /// + /// + /// + public virtual short GetInt16(int i) => 0; + + /// + /// + /// + /// + /// + public virtual int GetInt32(int i) => 0; + + /// + /// + /// + /// + /// + public virtual long GetInt64(int i) => 0; + + /// + /// + /// + /// + /// + public virtual int GetOrdinal(string name) => 0; + + /// + /// + /// + /// + public virtual DataTable GetSchemaTable() => null; + + /// + /// + /// + /// + /// + public virtual string GetString(int i) => string.Empty; /// - /// IMiniExcelDataReader Base Class + /// /// - public abstract class MiniExcelDataReaderBase : IMiniExcelDataReader + /// + /// + public virtual int GetValues(object[] values) => 0; + + /// + /// + /// + /// + /// + public virtual bool IsDBNull(int i) => false; + + /// + /// + /// + /// + public virtual bool NextResult() => false; + + /// + /// + /// + /// + /// + public virtual Task NextResultAsync(CancellationToken cancellationToken = default) { - /// - /// - /// - /// - /// - public virtual object this[int i] => null; - - /// - /// - /// - /// - /// - public virtual object this[string name] => null; - - /// - /// - /// - public virtual int Depth { get; } = 0; - - /// - /// - /// - public virtual bool IsClosed { get; } = false; - - /// - /// - /// - public virtual int RecordsAffected { get; } = 0; - - /// - /// - /// - public virtual int FieldCount { get; } - - /// - /// - /// - /// - /// - public virtual bool GetBoolean(int i) => false; - - /// - /// - /// - /// - /// - public virtual byte GetByte(int i) => byte.MinValue; - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public virtual long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferOffset, int length) => 0; - - /// - /// - /// - /// - /// - public virtual char GetChar(int i) => char.MinValue; - - /// - /// - /// - /// - /// - /// - /// - /// - /// - public virtual long GetChars(int i, long fieldOffset, char[] buffer, int bufferOffset, int length) => 0; - - /// - /// - /// - /// - /// - public virtual IDataReader GetData(int i) => null; - - /// - /// - /// - /// - /// - public virtual string GetDataTypeName(int i) => string.Empty; - - /// - /// - /// - /// - /// - public virtual DateTime GetDateTime(int i) => DateTime.MinValue; - - /// - /// - /// - /// - /// - public virtual decimal GetDecimal(int i) => 0; - - /// - /// - /// - /// - /// - public virtual double GetDouble(int i) => 0; - - /// - /// - /// - /// - /// - public virtual Type GetFieldType(int i) => null; - - /// - /// - /// - /// - /// - public virtual float GetFloat(int i) => 0f; - - /// - /// - /// - /// - /// - public virtual Guid GetGuid(int i) => Guid.Empty; - - /// - /// - /// - /// - /// - public virtual short GetInt16(int i) => 0; - - /// - /// - /// - /// - /// - public virtual int GetInt32(int i) => 0; - - /// - /// - /// - /// - /// - public virtual long GetInt64(int i) => 0; - - /// - /// - /// - /// - /// - public virtual int GetOrdinal(string name) => 0; - - /// - /// - /// - /// - public virtual DataTable GetSchemaTable() => null; - - /// - /// - /// - /// - /// - public virtual string GetString(int i) => string.Empty; - - /// - /// - /// - /// - /// - public virtual int GetValues(object[] values) => 0; - - /// - /// - /// - /// - /// - public virtual bool IsDBNull(int i) => false; - - /// - /// - /// - /// - public virtual bool NextResult() => false; - - /// - /// - /// - /// - /// - public virtual Task NextResultAsync(CancellationToken cancellationToken = default) + if (cancellationToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) - { - return MiniExcelTask.FromCanceled(cancellationToken); - } - else - { - try - { - return NextResult() ? Task.FromResult(true) : Task.FromResult(false); - } - catch (Exception e) - { - return MiniExcelTask.FromException(e); - } - } + return MiniExcelTask.FromCanceled(cancellationToken); } - - /// - /// - /// - /// - /// - public abstract string GetName(int i); - - /// - /// - /// - /// - /// - /// - public virtual Task GetNameAsync(int i, CancellationToken cancellationToken = default) + else { - if (cancellationToken.IsCancellationRequested) + try { - return MiniExcelTask.FromCanceled(cancellationToken); + return NextResult() ? Task.FromResult(true) : Task.FromResult(false); } - else + catch (Exception e) { - try - { - return Task.FromResult(GetName(i)); - } - catch (Exception e) - { - return MiniExcelTask.FromException(e); - } + return MiniExcelTask.FromException(e); } } + } + + /// + /// + /// + /// + /// + public abstract string GetName(int i); - /// - /// - /// - /// - /// - public abstract object GetValue(int i); - - /// - /// - /// - /// - /// - /// - public virtual Task GetValueAsync(int i, CancellationToken cancellationToken = default) + /// + /// + /// + /// + /// + /// + public virtual Task GetNameAsync(int i, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return MiniExcelTask.FromCanceled(cancellationToken); + } + else { - if (cancellationToken.IsCancellationRequested) + try { - return MiniExcelTask.FromCanceled(cancellationToken); + return Task.FromResult(GetName(i)); } - else + catch (Exception e) { - try - { - return Task.FromResult(GetValue(i)); - } - catch (Exception e) - { - return MiniExcelTask.FromException(e); - } + return MiniExcelTask.FromException(e); } } + } - /// - /// - /// - /// - public abstract bool Read(); - - /// - /// - /// - /// - /// - public virtual Task ReadAsync(CancellationToken cancellationToken = default) + /// + /// + /// + /// + /// + public abstract object GetValue(int i); + + /// + /// + /// + /// + /// + /// + public virtual Task GetValueAsync(int i, CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) { - if (cancellationToken.IsCancellationRequested) + return MiniExcelTask.FromCanceled(cancellationToken); + } + else + { + try { - return MiniExcelTask.FromCanceled(cancellationToken); + return Task.FromResult(GetValue(i)); } - else + catch (Exception e) { - try - { - return Read() ? Task.FromResult(true) : Task.FromResult(false); - } - catch (Exception e) - { - return MiniExcelTask.FromException(e); - } + return MiniExcelTask.FromException(e); } } + } - /// - /// - /// - public virtual void Close() - { + /// + /// + /// + /// + public abstract bool Read(); + /// + /// + /// + /// + /// + public virtual Task ReadAsync(CancellationToken cancellationToken = default) + { + if (cancellationToken.IsCancellationRequested) + { + return MiniExcelTask.FromCanceled(cancellationToken); } - - /// - /// - /// - /// - public virtual Task CloseAsync() + else { try { - Close(); - return MiniExcelTask.CompletedTask; + return Read() ? Task.FromResult(true) : Task.FromResult(false); } catch (Exception e) { - return MiniExcelTask.FromException(e); + return MiniExcelTask.FromException(e); } } + } - /// - /// - /// - public void Dispose() + /// + /// + /// + public virtual void Close() + { + + } + + /// + /// + /// + /// + public virtual Task CloseAsync() + { + try { - Dispose(true); - GC.SuppressFinalize(this); + Close(); + return MiniExcelTask.CompletedTask; } - -#if NET8_0_OR_GREATER - /// - /// - /// - /// - /// - public virtual ValueTask DisposeAsync() + catch (Exception e) { - Dispose(); - return default; + return MiniExcelTask.FromException(e); } + } + + /// + /// + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + +#if NET8_0_OR_GREATER + /// + /// + /// + /// + /// + public virtual ValueTask DisposeAsync() + { + Dispose(); + return default; + } #endif - /// - /// - /// - /// - protected virtual void Dispose(bool disposing) + /// + /// + /// + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) { - if (disposing) - { - Close(); - } + Close(); } } -} +} \ No newline at end of file diff --git a/src/MiniExcel/MiniExcelTask.cs b/src/MiniExcel/MiniExcelTask.cs index 06f8d414..c6807607 100644 --- a/src/MiniExcel/MiniExcelTask.cs +++ b/src/MiniExcel/MiniExcelTask.cs @@ -1,59 +1,54 @@ -using System; -using System.Threading; -using System.Threading.Tasks; +namespace MiniExcelLibs; -namespace MiniExcelLibs +internal class MiniExcelTask { - internal class MiniExcelTask - { #if NET45 public static Task CompletedTask = Task.FromResult(0); #else - public static Task CompletedTask = Task.CompletedTask; + public static Task CompletedTask = Task.CompletedTask; #endif - public static Task FromException(Exception exception) - { + public static Task FromException(Exception exception) + { #if NET45 var tcs = new TaskCompletionSource(); tcs.SetException(exception); return tcs.Task; #else - return Task.FromException(exception); + return Task.FromException(exception); #endif - } + } - public static Task FromException(Exception exception) - { + public static Task FromException(Exception exception) + { #if NET45 var tcs = new TaskCompletionSource(); tcs.SetException(exception); return tcs.Task; #else - return Task.FromException(exception); + return Task.FromException(exception); #endif - } + } - public static Task FromCanceled(CancellationToken cancellationToken) - { + public static Task FromCanceled(CancellationToken cancellationToken) + { #if NET45 var tcs = new TaskCompletionSource(); cancellationToken.Register(() => tcs.SetCanceled()); return tcs.Task; #else - return Task.FromCanceled(cancellationToken); + return Task.FromCanceled(cancellationToken); #endif - } + } - public static Task FromCanceled(CancellationToken cancellationToken) - { + public static Task FromCanceled(CancellationToken cancellationToken) + { #if NET45 var tcs = new TaskCompletionSource(); cancellationToken.Register(() => tcs.SetCanceled()); return tcs.Task; #else - return Task.FromCanceled(cancellationToken); + return Task.FromCanceled(cancellationToken); #endif - } } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Config.cs b/src/MiniExcel/OpenXml/Config.cs index 32fa409f..94cabc82 100644 --- a/src/MiniExcel/OpenXml/Config.cs +++ b/src/MiniExcel/OpenXml/Config.cs @@ -1,11 +1,10 @@ -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal static class Config { - internal static class Config - { - public const string SpreadsheetmlXmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; - public const string SpreadsheetmlXmlStrictns = "http://purl.oclc.org/ooxml/spreadsheetml/main"; - public const string SpreadsheetmlXmlRelationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; - public const string SpreadsheetmlXmlStrictRelationshipns = "http://purl.oclc.org/ooxml/officeDocument/relationships"; - public const string SpreadsheetmlXml_x14ac = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"; - } + public const string SpreadsheetmlXmlns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main"; + public const string SpreadsheetmlXmlStrictns = "http://purl.oclc.org/ooxml/spreadsheetml/main"; + public const string SpreadsheetmlXmlRelationshipns = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; + public const string SpreadsheetmlXmlStrictRelationshipns = "http://purl.oclc.org/ooxml/officeDocument/relationships"; + public const string SpreadsheetmlXml_x14ac = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac"; } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Constants/ExcelContentTypes.cs b/src/MiniExcel/OpenXml/Constants/ExcelContentTypes.cs index 52ea48e4..bcb5f9fa 100644 --- a/src/MiniExcel/OpenXml/Constants/ExcelContentTypes.cs +++ b/src/MiniExcel/OpenXml/Constants/ExcelContentTypes.cs @@ -1,12 +1,11 @@ -namespace MiniExcelLibs.OpenXml.Constants +namespace MiniExcelLibs.OpenXml.Constants; + +internal static class ExcelContentTypes { - internal static class ExcelContentTypes - { - internal const string Relationships = "application/vnd.openxmlformats-package.relationships+xml"; - internal const string SharedStrings = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; - internal const string Worksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; - internal const string Styles = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"; - internal const string Drawing = "application/vnd.openxmlformats-officedocument.drawing+xml"; - internal const string Workbook = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; - } -} + internal const string Relationships = "application/vnd.openxmlformats-package.relationships+xml"; + internal const string SharedStrings = "application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml"; + internal const string Worksheet = "application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"; + internal const string Styles = "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"; + internal const string Drawing = "application/vnd.openxmlformats-officedocument.drawing+xml"; + internal const string Workbook = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"; +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Constants/ExcelFileNames.cs b/src/MiniExcel/OpenXml/Constants/ExcelFileNames.cs index 2ade87fc..982c22eb 100644 --- a/src/MiniExcel/OpenXml/Constants/ExcelFileNames.cs +++ b/src/MiniExcel/OpenXml/Constants/ExcelFileNames.cs @@ -1,17 +1,16 @@ -namespace MiniExcelLibs.OpenXml.Constants +namespace MiniExcelLibs.OpenXml.Constants; + +internal static class ExcelFileNames { - internal static class ExcelFileNames - { - internal const string Rels = "_rels/.rels"; - internal const string SharedStrings = "xl/sharedStrings.xml"; + internal const string Rels = "_rels/.rels"; + internal const string SharedStrings = "xl/sharedStrings.xml"; - internal const string ContentTypes = "[Content_Types].xml"; - internal const string Styles = "xl/styles.xml"; - internal const string Workbook = "xl/workbook.xml"; - internal const string WorkbookRels = "xl/_rels/workbook.xml.rels"; + internal const string ContentTypes = "[Content_Types].xml"; + internal const string Styles = "xl/styles.xml"; + internal const string Workbook = "xl/workbook.xml"; + internal const string WorkbookRels = "xl/_rels/workbook.xml.rels"; - internal static string SheetRels(int sheetId) => $"xl/worksheets/_rels/sheet{sheetId}.xml.rels"; - internal static string Drawing(int sheetIndex) => $"xl/drawings/drawing{sheetIndex + 1}.xml"; - internal static string DrawingRels(int sheetIndex) => $"xl/drawings/_rels/drawing{sheetIndex + 1}.xml.rels"; - } -} + internal static string SheetRels(int sheetId) => $"xl/worksheets/_rels/sheet{sheetId}.xml.rels"; + internal static string Drawing(int sheetIndex) => $"xl/drawings/drawing{sheetIndex + 1}.xml"; + internal static string DrawingRels(int sheetIndex) => $"xl/drawings/_rels/drawing{sheetIndex + 1}.xml.rels"; +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Constants/ExcelXml.cs b/src/MiniExcel/OpenXml/Constants/ExcelXml.cs index 35683412..9975c94a 100644 --- a/src/MiniExcel/OpenXml/Constants/ExcelXml.cs +++ b/src/MiniExcel/OpenXml/Constants/ExcelXml.cs @@ -1,34 +1,34 @@ using MiniExcelLibs.OpenXml.Models; -namespace MiniExcelLibs.OpenXml.Constants +namespace MiniExcelLibs.OpenXml.Constants; + +internal static class ExcelXml { - internal static class ExcelXml + static ExcelXml() { - static ExcelXml() - { - DefaultRels = ExcelOpenXmlUtils.MinifyXml(DefaultRels); - DefaultWorkbookXml = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXml); - DefaultWorkbookXmlRels = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXmlRels); - DefaultSheetRelXml = ExcelOpenXmlUtils.MinifyXml(DefaultSheetRelXml); - DefaultDrawing = ExcelOpenXmlUtils.MinifyXml(DefaultDrawing); - } + DefaultRels = ExcelOpenXmlUtils.MinifyXml(DefaultRels); + DefaultWorkbookXml = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXml); + DefaultWorkbookXmlRels = ExcelOpenXmlUtils.MinifyXml(DefaultWorkbookXmlRels); + DefaultSheetRelXml = ExcelOpenXmlUtils.MinifyXml(DefaultSheetRelXml); + DefaultDrawing = ExcelOpenXmlUtils.MinifyXml(DefaultDrawing); + } - internal const string EmptySheetXml = @""; + internal const string EmptySheetXml = @""; - internal static readonly string DefaultRels = @" + internal static readonly string DefaultRels = @" "; - internal static readonly string DefaultWorkbookXmlRels = @" + internal static readonly string DefaultWorkbookXmlRels = @" {{sheets}} "; - internal static readonly string DefaultWorkbookXml = @" + internal static readonly string DefaultWorkbookXml = @" @@ -36,40 +36,40 @@ static ExcelXml() "; - internal static readonly string DefaultSheetRelXml = @" + internal static readonly string DefaultSheetRelXml = @" {{format}} "; - internal static readonly string DefaultDrawing = @" + internal static readonly string DefaultDrawing = @" {{format}} "; - internal const string DefaultDrawingXmlRels = @" + internal const string DefaultDrawingXmlRels = @" {{format}} "; - internal const string DefaultSharedString = ""; + internal const string DefaultSharedString = ""; - internal const string StartTypes = @""; - internal static string ContentType(string contentType, string partName) => $""; - internal const string EndTypes = ""; + internal const string StartTypes = @""; + internal static string ContentType(string contentType, string partName) => $""; + internal const string EndTypes = ""; - internal static string WorksheetRelationship(SheetDto sheetDto) - => $@""; + internal static string WorksheetRelationship(SheetDto sheetDto) + => $@""; - internal static string ImageRelationship(FileDto image) - => $@""; + internal static string ImageRelationship(FileDto image) + => $@""; - internal static string DrawingRelationship(int sheetId) - => $@""; + internal static string DrawingRelationship(int sheetId) + => $@""; - internal static string DrawingXml(FileDto file, int fileIndex) - => $@" + internal static string DrawingXml(FileDto file, int fileIndex) + => $@" {file.CellIndex - 1/* why -1 : https://user-images.githubusercontent.com/12729184/150460189-f08ed939-44d4-44e1-be6e-9c533ece6be8.png*/} 0 @@ -103,9 +103,7 @@ internal static string DrawingXml(FileDto file, int fileIndex) "; - internal static string Sheet(SheetDto sheetDto, int sheetId) - => $@""; - - } + internal static string Sheet(SheetDto sheetDto, int sheetId) + => $@""; -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs b/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs index 37ece9d3..1faa9046 100644 --- a/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs +++ b/src/MiniExcel/OpenXml/Constants/WorksheetXml.cs @@ -1,68 +1,67 @@ using System.Globalization; using MiniExcelLibs.Attributes; -namespace MiniExcelLibs.OpenXml.Constants +namespace MiniExcelLibs.OpenXml.Constants; + +internal static class WorksheetXml { - internal static class WorksheetXml - { - internal const string StartWorksheet = @""; - internal const string StartWorksheetWithRelationship = @""; - internal const string EndWorksheet = ""; + internal const string StartWorksheet = @""; + internal const string StartWorksheetWithRelationship = @""; + internal const string EndWorksheet = ""; - internal const string StartDimension = " $"{StartDimension}{dimensionRef}\" />"; + internal const string StartDimension = " $"{StartDimension}{dimensionRef}\" />"; - internal const string StartSheetViews = ""; - internal const string EndSheetViews = ""; + internal const string StartSheetViews = ""; + internal const string EndSheetViews = ""; - internal static string StartSheetView(int tabSelected = 0, int workbookViewId = 0, bool rightToLeft = false) - => $""; - internal const string EndSheetView = ""; + internal static string StartSheetView(int tabSelected = 0, int workbookViewId = 0, bool rightToLeft = false) + => $""; + internal const string EndSheetView = ""; - internal const string StartSheetData = ""; - internal const string EndSheetData = ""; + internal const string StartSheetData = ""; + internal const string EndSheetData = ""; - internal static string StartPane( int? xSplit, int? ySplit, string topLeftCell, string activePane, string state ) - => string.Concat( - ""); + internal static string StartPane( int? xSplit, int? ySplit, string topLeftCell, string activePane, string state ) + => string.Concat( + ""); - internal static string PaneSelection( string pane, string activeCell, string sqref) - => string.Concat( - ""); + internal static string PaneSelection( string pane, string activeCell, string sqref) + => string.Concat( + ""); - internal static string StartRow(int rowIndex) => $""; - internal const string EndRow = ""; - internal const string StartCols = ""; + internal static string StartRow(int rowIndex) => $""; + internal const string EndRow = ""; + internal const string StartCols = ""; - internal static string Column(int colIndex, double columnWidth, bool hidden = false) - => $@""; + internal const string EndCols = ""; - internal static string EmptyCell(string cellReference, string styleIndex) - => $""; + internal static string EmptyCell(string cellReference, string styleIndex) + => $""; - //t check avoid format error ![image](https://user-images.githubusercontent.com/12729184/118770190-9eee3480-b8b3-11eb-9f5a-87a439f5e320.png) - internal static string Cell(string cellReference, string cellType, string styleIndex, string cellValue, bool preserveSpace = false, ColumnType columnType = ColumnType.Value) - => $"{cellValue}"; + //t check avoid format error ![image](https://user-images.githubusercontent.com/12729184/118770190-9eee3480-b8b3-11eb-9f5a-87a439f5e320.png) + internal static string Cell(string cellReference, string cellType, string styleIndex, string cellValue, bool preserveSpace = false, ColumnType columnType = ColumnType.Value) + => $"{cellValue}"; - internal static string Autofilter(string dimensionRef) => $""; - internal static string Drawing(int sheetIndex) => $""; - } -} + internal static string Autofilter(string dimensionRef) => $""; + internal static string Drawing(int sheetIndex) => $""; +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.Async.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.Async.cs index 2ad5ff99..667d56d5 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.Async.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.Async.cs @@ -1,39 +1,34 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; +namespace MiniExcelLibs.OpenXml; -namespace MiniExcelLibs.OpenXml +internal partial class ExcelOpenXmlSheetReader : IExcelReader { - internal partial class ExcelOpenXmlSheetReader : IExcelReader + public async Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default) { - public async Task>> QueryAsync(bool useHeaderRow, string sheetName, string startCell, CancellationToken cancellationToken = default) - { - return await Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken).ConfigureAwait(false); - } + return await Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken).ConfigureAwait(false); + } - public async Task> QueryAsync(string sheetName, string startCell, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => Query(sheetName, startCell, hasHeader), cancellationToken).ConfigureAwait(false); - } + public async Task> QueryAsync(string sheetName, string startCell, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => Query(sheetName, startCell, hasHeader), cancellationToken).ConfigureAwait(false); + } - public async Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default) - { - return await Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken).ConfigureAwait(false); - } + public async Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, string startCell, string endCell, CancellationToken cancellationToken = default) + { + return await Task.Run(() => Query(useHeaderRow, sheetName, startCell), cancellationToken).ConfigureAwait(false); + } - public async Task> QueryRangeAsync(string sheetName, string startCell, string endCell, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => QueryRange(sheetName, startCell, endCell, hasHeader), cancellationToken).ConfigureAwait(false); - } + public async Task> QueryRangeAsync(string sheetName, string startCell, string endCell, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => QueryRange(sheetName, startCell, endCell, hasHeader), cancellationToken).ConfigureAwait(false); + } - public async Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default) - { - return await Task.Run(() => QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), cancellationToken).ConfigureAwait(false); - } + public async Task>> QueryRangeAsync(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, CancellationToken cancellationToken = default) + { + return await Task.Run(() => QueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), cancellationToken).ConfigureAwait(false); + } - public async Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() - { - return await Task.Run(() => QueryRange(sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, hasHeader), cancellationToken).ConfigureAwait(false); - } + public async Task> QueryRangeAsync(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader = true, CancellationToken cancellationToken = default) where T : class, new() + { + return await Task.Run(() => QueryRange(sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex, hasHeader), cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs index aac17148..74e90c54 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetReader.cs @@ -1,865 +1,742 @@ using MiniExcelLibs.OpenXml.Models; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; -using System.IO; using System.IO.Compression; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; using System.Xml; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal partial class ExcelOpenXmlSheetReader : IExcelReader { - internal partial class ExcelOpenXmlSheetReader : IExcelReader + private bool _disposed = false; + private static readonly string[] _ns = { Config.SpreadsheetmlXmlns, Config.SpreadsheetmlXmlStrictns }; + private static readonly string[] _relationshiopNs = { Config.SpreadsheetmlXmlRelationshipns, Config.SpreadsheetmlXmlStrictRelationshipns }; + private List _sheetRecords; + internal IDictionary _sharedStrings; + private MergeCells _mergeCells; + private ExcelOpenXmlStyles _style; + internal readonly ExcelOpenXmlZip _archive; + private readonly OpenXmlConfiguration _config; + + private static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings { - private bool _disposed = false; - private static readonly string[] _ns = { Config.SpreadsheetmlXmlns, Config.SpreadsheetmlXmlStrictns }; - private static readonly string[] _relationshiopNs = { Config.SpreadsheetmlXmlRelationshipns, Config.SpreadsheetmlXmlStrictRelationshipns }; - private List _sheetRecords; - internal IDictionary _sharedStrings; - private MergeCells _mergeCells; - private ExcelOpenXmlStyles _style; - internal readonly ExcelOpenXmlZip _archive; - private readonly OpenXmlConfiguration _config; - - private static readonly XmlReaderSettings _xmlSettings = new XmlReaderSettings - { - IgnoreComments = true, - IgnoreWhitespace = true, - XmlResolver = null - }; + IgnoreComments = true, + IgnoreWhitespace = true, + XmlResolver = null + }; - public ExcelOpenXmlSheetReader(Stream stream, IConfiguration configuration, bool isUpdateMode = true) - { - _archive = new ExcelOpenXmlZip(stream); - _config = (OpenXmlConfiguration)configuration ?? OpenXmlConfiguration.DefaultConfig; - SetSharedStrings(); - } + public ExcelOpenXmlSheetReader(Stream stream, IConfiguration configuration, bool isUpdateMode = true) + { + _archive = new ExcelOpenXmlZip(stream); + _config = (OpenXmlConfiguration)configuration ?? OpenXmlConfiguration.DefaultConfig; + SetSharedStrings(); + } - public IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell) - { - return QueryRange(useHeaderRow, sheetName, startCell, ""); - } + public IEnumerable> Query(bool useHeaderRow, string sheetName, string startCell) + { + return QueryRange(useHeaderRow, sheetName, startCell, ""); + } - public IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new() - { - if (sheetName == null) - sheetName = CustomPropertyHelper.GetExcellSheetInfo(typeof(T), _config)?.ExcelSheetName; + public IEnumerable Query(string sheetName, string startCell, bool hasHeader) where T : class, new() + { + if (sheetName == null) + sheetName = CustomPropertyHelper.GetExcellSheetInfo(typeof(T), _config)?.ExcelSheetName; - //Todo: Find a way if possible to remove the 'hasHeader' parameter to check whether or not to include - // the first row in the result set in favor of modifying the already present 'useHeaderRow' to do the same job - return QueryImpl(Query(false, sheetName, startCell), startCell, hasHeader, _config); - } + //Todo: Find a way if possible to remove the 'hasHeader' parameter to check whether or not to include + // the first row in the result set in favor of modifying the already present 'useHeaderRow' to do the same job + return QueryImpl(Query(false, sheetName, startCell), startCell, hasHeader, _config); + } - public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell) + public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, string startCell, string endCell) + { + if (!ReferenceHelper.ParseReference(startCell, out var startColumnIndex, out var startRowIndex)) { - if (!ReferenceHelper.ParseReference(startCell, out var startColumnIndex, out var startRowIndex)) + throw new InvalidDataException($"Value {startCell} is not a valid cell reference."); + } + // convert to 0-based + startColumnIndex--; + startRowIndex--; + + // endCell is allowed to be empty to query for all rows and columns + int? endColumnIndex = null; + int? endRowIndex = null; + if (!string.IsNullOrWhiteSpace(endCell)) + { + if (!ReferenceHelper.ParseReference(endCell, out int cIndex, out int rIndex)) { - throw new InvalidDataException($"Value {startCell} is not a valid cell reference."); + throw new InvalidDataException($"Value {endCell} is not a valid cell reference."); } + // convert to 0-based - startColumnIndex--; - startRowIndex--; + endColumnIndex = cIndex - 1; + endRowIndex = rIndex - 1; + } - // endCell is allowed to be empty to query for all rows and columns - int? endColumnIndex = null; - int? endRowIndex = null; - if (!string.IsNullOrWhiteSpace(endCell)) - { - if (!ReferenceHelper.ParseReference(endCell, out int cIndex, out int rIndex)) - { - throw new InvalidDataException($"Value {endCell} is not a valid cell reference."); - } + return InternalQueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); + } - // convert to 0-based - endColumnIndex = cIndex - 1; - endRowIndex = rIndex - 1; - } + public IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new() + { + return QueryImpl(QueryRange(false, sheetName, startCell, endCell), startCell, hasHeader, this._config); + } - return InternalQueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); + public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) + { + if (startRowIndex <= 0) + { + throw new ArgumentOutOfRangeException(nameof(startRowIndex), "Start row index is 1-based and must be greater than 0."); } - - public IEnumerable QueryRange(string sheetName, string startCell, string endCell, bool hasHeader) where T : class, new() + if (startColumnIndex <= 0) { - return QueryImpl(QueryRange(false, sheetName, startCell, endCell), startCell, hasHeader, this._config); + throw new ArgumentOutOfRangeException(nameof(startColumnIndex), "Start column index is 1-based and must be greater than 0."); } + // convert to 0-based + startColumnIndex--; + startRowIndex--; - public IEnumerable> QueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) + if (endRowIndex.HasValue) { - if (startRowIndex <= 0) + if (endRowIndex.Value <= 0) { - throw new ArgumentOutOfRangeException(nameof(startRowIndex), "Start row index is 1-based and must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(endRowIndex), "End row index is 1-based and must be greater than 0."); } - if (startColumnIndex <= 0) + // convert to 0-based + endRowIndex--; + } + if (endColumnIndex.HasValue) + { + if (endColumnIndex.Value <= 0) { - throw new ArgumentOutOfRangeException(nameof(startColumnIndex), "Start column index is 1-based and must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(endColumnIndex), "End column index is 1-based and must be greater than 0."); } // convert to 0-based - startColumnIndex--; - startRowIndex--; + endColumnIndex--; + } - if (endRowIndex.HasValue) - { - if (endRowIndex.Value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(endRowIndex), "End row index is 1-based and must be greater than 0."); - } - // convert to 0-based - endRowIndex--; - } - if (endColumnIndex.HasValue) - { - if (endColumnIndex.Value <= 0) - { - throw new ArgumentOutOfRangeException(nameof(endColumnIndex), "End column index is 1-based and must be greater than 0."); - } - // convert to 0-based - endColumnIndex--; - } + return InternalQueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); + } - return InternalQueryRange(useHeaderRow, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex); - } + public IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new() + { + return QueryImpl(QueryRange(false, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), ReferenceHelper.ConvertXyToCell(startColumnIndex, startRowIndex), hasHeader, _config); + } - public IEnumerable QueryRange(string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex, bool hasHeader) where T : class, new() + internal IEnumerable> InternalQueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) + { + var sheetEntry = GetSheetEntry(sheetName); + + // TODO: need to optimize performance + // Q. why need 3 times openstream merge one open read? A. no, zipstream can't use position = 0 + + if (_config.FillMergedCells && !TryGetMergeCells(sheetEntry, out _mergeCells)) { - return QueryImpl(QueryRange(false, sheetName, startRowIndex, startColumnIndex, endRowIndex, endColumnIndex), ReferenceHelper.ConvertXyToCell(startColumnIndex, startRowIndex), hasHeader, _config); + yield break; } - internal IEnumerable> InternalQueryRange(bool useHeaderRow, string sheetName, int startRowIndex, int startColumnIndex, int? endRowIndex, int? endColumnIndex) + if (!TryGetMaxRowColumnIndex(sheetEntry, out var withoutCR, out var maxRowIndex, out var maxColumnIndex)) { - var sheetEntry = GetSheetEntry(sheetName); + yield break; + } - // TODO: need to optimize performance - // Q. why need 3 times openstream merge one open read? A. no, zipstream can't use position = 0 + if (endColumnIndex.HasValue) + { + maxColumnIndex = endColumnIndex.Value; + } - if (_config.FillMergedCells && !TryGetMergeCells(sheetEntry, out _mergeCells)) - { + using (var sheetStream = sheetEntry.Open()) + using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) + { + if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) yield break; - } - if (!TryGetMaxRowColumnIndex(sheetEntry, out var withoutCR, out var maxRowIndex, out var maxColumnIndex)) - { + if (!XmlReaderHelper.ReadFirstContent(reader)) yield break; - } - - if (endColumnIndex.HasValue) - { - maxColumnIndex = endColumnIndex.Value; - } - using (var sheetStream = sheetEntry.Open()) - using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) + while (!reader.EOF) { - if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) - yield break; - - if (!XmlReaderHelper.ReadFirstContent(reader)) - yield break; - - while (!reader.EOF) + if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - var headRows = new Dictionary(); - int rowIndex = -1; - bool isFirstRow = true; - while (!reader.EOF) + var headRows = new Dictionary(); + int rowIndex = -1; + bool isFirstRow = true; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "row", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "row", _ns)) - { - var nextRowIndex = rowIndex + 1; - if (int.TryParse(reader.GetAttribute("r"), out int arValue)) - rowIndex = arValue - 1; // The row attribute is 1-based - else - rowIndex++; - - if (rowIndex < startRowIndex) - { - if (XmlReaderHelper.ReadFirstContent(reader)) - { - XmlReaderHelper.SkipToNextSameLevelDom(reader); - } - - continue; - } - if (endRowIndex.HasValue && rowIndex > endRowIndex.Value) - { - break; - } + var nextRowIndex = rowIndex + 1; + if (int.TryParse(reader.GetAttribute("r"), out int arValue)) + rowIndex = arValue - 1; // The row attribute is 1-based + else + rowIndex++; - foreach (var row in QueryRow(reader, isFirstRow, startRowIndex, nextRowIndex, rowIndex, startColumnIndex, endColumnIndex, maxColumnIndex, withoutCR, useHeaderRow, headRows, _mergeCells)) + if (rowIndex < startRowIndex) + { + if (XmlReaderHelper.ReadFirstContent(reader)) { - if (isFirstRow) - { - isFirstRow = false; // for startcell logic - if (useHeaderRow) - continue; - } - yield return row; + XmlReaderHelper.SkipToNextSameLevelDom(reader); } + + continue; } - else if (!XmlReaderHelper.SkipContent(reader)) + if (endRowIndex.HasValue && rowIndex > endRowIndex.Value) { break; } + + foreach (var row in QueryRow(reader, isFirstRow, startRowIndex, nextRowIndex, rowIndex, startColumnIndex, endColumnIndex, maxColumnIndex, withoutCR, useHeaderRow, headRows, _mergeCells)) + { + if (isFirstRow) + { + isFirstRow = false; // for startcell logic + if (useHeaderRow) + continue; + } + yield return row; + } + } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; } - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; } } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; + } } } + } - private IEnumerable> QueryRow( - XmlReader reader, - bool isFirstRow, - int startRowIndex, - int nextRowIndex, - int rowIndex, - int startColumnIndex, - int? endColumnIndex, - int maxColumnIndex, - bool withoutCR, - bool useHeaderRow, - Dictionary headRows, - MergeCells mergeCells) + private IEnumerable> QueryRow( + XmlReader reader, + bool isFirstRow, + int startRowIndex, + int nextRowIndex, + int rowIndex, + int startColumnIndex, + int? endColumnIndex, + int maxColumnIndex, + bool withoutCR, + bool useHeaderRow, + Dictionary headRows, + MergeCells mergeCells) + { + // fill empty rows + if (!_config.IgnoreEmptyRows) { - // fill empty rows - if (!_config.IgnoreEmptyRows) + var expectedRowIndex = isFirstRow ? startRowIndex : nextRowIndex; + if (startRowIndex <= expectedRowIndex && expectedRowIndex < rowIndex) { - var expectedRowIndex = isFirstRow ? startRowIndex : nextRowIndex; - if (startRowIndex <= expectedRowIndex && expectedRowIndex < rowIndex) + for (int i = expectedRowIndex; i < rowIndex; i++) { - for (int i = expectedRowIndex; i < rowIndex; i++) - { - yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); - } + yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); } } + } - // row -> c, must after `if (nextRowIndex < rowIndex)` condition code, eg. The first empty row has no xml element,and the second row xml element is - if (!XmlReaderHelper.ReadFirstContent(reader) && !_config.IgnoreEmptyRows) - { - //Fill in case of self closed empty row tag eg. - yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); - yield break; - } + // row -> c, must after `if (nextRowIndex < rowIndex)` condition code, eg. The first empty row has no xml element,and the second row xml element is + if (!XmlReaderHelper.ReadFirstContent(reader) && !_config.IgnoreEmptyRows) + { + //Fill in case of self closed empty row tag eg. + yield return GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); + yield break; + } - var cell = GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); - var columnIndex = withoutCR ? -1 : 0; - while (!reader.EOF) + var cell = GetCell(useHeaderRow, maxColumnIndex, headRows, startColumnIndex); + var columnIndex = withoutCR ? -1 : 0; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) - { - var aS = reader.GetAttribute("s"); - var aR = reader.GetAttribute("r"); - var aT = reader.GetAttribute("t"); - var cellAndColumn = ReadCellAndSetColumnIndex(reader, columnIndex, withoutCR, startColumnIndex, aR, aT); + var aS = reader.GetAttribute("s"); + var aR = reader.GetAttribute("r"); + var aT = reader.GetAttribute("t"); + var cellAndColumn = ReadCellAndSetColumnIndex(reader, columnIndex, withoutCR, startColumnIndex, aR, aT); - var cellValue = cellAndColumn.CellValue; - columnIndex = cellAndColumn.ColumnIndex; + var cellValue = cellAndColumn.CellValue; + columnIndex = cellAndColumn.ColumnIndex; - if (_config.FillMergedCells) + if (_config.FillMergedCells) + { + if (mergeCells.MergesValues.ContainsKey(aR)) { - if (mergeCells.MergesValues.ContainsKey(aR)) - { - mergeCells.MergesValues[aR] = cellValue; - } - else if (mergeCells.MergesMap.TryGetValue(aR, out var mergeKey)) - { - mergeCells.MergesValues.TryGetValue(mergeKey, out cellValue); - } + mergeCells.MergesValues[aR] = cellValue; } - - if (columnIndex < startColumnIndex || (endColumnIndex.HasValue && columnIndex > endColumnIndex.Value)) - continue; - - if (!string.IsNullOrEmpty(aS)) // if c with s meaning is custom style need to check type by xl/style.xml + else if (mergeCells.MergesMap.TryGetValue(aR, out var mergeKey)) { - int xfIndex = -1; - if (int.TryParse(aS, NumberStyles.Any, CultureInfo.InvariantCulture, - out var styleIndex)) - xfIndex = styleIndex; + mergeCells.MergesValues.TryGetValue(mergeKey, out cellValue); + } + } - // only when have s attribute then load styles xml data - if (_style == null) - _style = new ExcelOpenXmlStyles(_archive); + if (columnIndex < startColumnIndex || (endColumnIndex.HasValue && columnIndex > endColumnIndex.Value)) + continue; - cellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue); - } + if (!string.IsNullOrEmpty(aS)) // if c with s meaning is custom style need to check type by xl/style.xml + { + int xfIndex = -1; + if (int.TryParse(aS, NumberStyles.Any, CultureInfo.InvariantCulture, + out var styleIndex)) + xfIndex = styleIndex; + + // only when have s attribute then load styles xml data + if (_style == null) + _style = new ExcelOpenXmlStyles(_archive); - SetCellsValueAndHeaders(cellValue, useHeaderRow, headRows, isFirstRow, cell, columnIndex); + cellValue = _style.ConvertValueByStyleFormat(xfIndex, cellValue); } - else if (!XmlReaderHelper.SkipContent(reader)) - break; + + SetCellsValueAndHeaders(cellValue, useHeaderRow, headRows, isFirstRow, cell, columnIndex); } - yield return cell; + else if (!XmlReaderHelper.SkipContent(reader)) + break; } + yield return cell; + } - public static IEnumerable QueryImpl(IEnumerable> values, string startCell, bool hasHeader, Configuration configuration) where T : class, new() - { - var type = typeof(T); + public static IEnumerable QueryImpl(IEnumerable> values, string startCell, bool hasHeader, Configuration configuration) where T : class, new() + { + var type = typeof(T); - //TODO:need to optimize - List props = null; - Dictionary headersDic = null; - string[] keys = null; - var first = true; - var rowIndex = 0; + //TODO:need to optimize + List props = null; + Dictionary headersDic = null; + string[] keys = null; + var first = true; + var rowIndex = 0; - foreach (var item in values) + foreach (var item in values) + { + if (first) { - if (first) - { - keys = item.Keys.ToArray(); - var trimColumnNames = (configuration as OpenXmlConfiguration)?.TrimColumnNames ?? false; - headersDic = CustomPropertyHelper.GetHeaders(item, trimColumnNames); + keys = item.Keys.ToArray(); + var trimColumnNames = (configuration as OpenXmlConfiguration)?.TrimColumnNames ?? false; + headersDic = CustomPropertyHelper.GetHeaders(item, trimColumnNames); - //TODO: alert don't duplicate column name - props = CustomPropertyHelper.GetExcelCustomPropertyInfos(type, keys, configuration); - first = false; + //TODO: alert don't duplicate column name + props = CustomPropertyHelper.GetExcelCustomPropertyInfos(type, keys, configuration); + first = false; - if (hasHeader) - continue; - } + if (hasHeader) + continue; + } - var v = new T(); - foreach (var pInfo in props) + var v = new T(); + foreach (var pInfo in props) + { + if (pInfo.ExcelColumnAliases != null) { - if (pInfo.ExcelColumnAliases != null) + foreach (var alias in pInfo.ExcelColumnAliases) { - foreach (var alias in pInfo.ExcelColumnAliases) + if (headersDic.TryGetValue(alias, out var columnId)) { - if (headersDic.TryGetValue(alias, out var columnId)) - { - var columnName = keys[columnId]; - item.TryGetValue(columnName, out var aliasItemValue); + var columnName = keys[columnId]; + item.TryGetValue(columnName, out var aliasItemValue); - if (aliasItemValue != null) - { - var newAliasValue = TypeHelper.TypeMapping(v, pInfo, aliasItemValue, rowIndex, startCell, configuration); - } + if (aliasItemValue != null) + { + var newAliasValue = TypeHelper.TypeMapping(v, pInfo, aliasItemValue, rowIndex, startCell, configuration); } } } + } - //Q: Why need to check every time? A: it needs to check everytime, because it's dictionary - object itemValue = null; - if (pInfo.ExcelIndexName != null && keys.Contains(pInfo.ExcelIndexName)) - { - item.TryGetValue(pInfo.ExcelIndexName, out itemValue); - } - else if (headersDic.TryGetValue(pInfo.ExcelColumnName, out var columnId)) - { - var columnName = keys[columnId]; - item.TryGetValue(columnName, out itemValue); - } + //Q: Why need to check every time? A: it needs to check everytime, because it's dictionary + object itemValue = null; + if (pInfo.ExcelIndexName != null && keys.Contains(pInfo.ExcelIndexName)) + { + item.TryGetValue(pInfo.ExcelIndexName, out itemValue); + } + else if (headersDic.TryGetValue(pInfo.ExcelColumnName, out var columnId)) + { + var columnName = keys[columnId]; + item.TryGetValue(columnName, out itemValue); + } - if (itemValue != null) - { - var newValue = TypeHelper.TypeMapping(v, pInfo, itemValue, rowIndex, startCell, configuration); - } + if (itemValue != null) + { + var newValue = TypeHelper.TypeMapping(v, pInfo, itemValue, rowIndex, startCell, configuration); } - rowIndex++; - yield return v; } + rowIndex++; + yield return v; } + } - private ZipArchiveEntry GetSheetEntry(string sheetName) + private ZipArchiveEntry GetSheetEntry(string sheetName) + { + // if sheets count > 1 need to read xl/_rels/workbook.xml.rels + var sheets = _archive.entries + .Where(w => w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase) || w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)) + .ToArray(); + ZipArchiveEntry sheetEntry = null; + if (sheetName != null) { - // if sheets count > 1 need to read xl/_rels/workbook.xml.rels - var sheets = _archive.entries - .Where(w => w.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase) || w.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)) - .ToArray(); - ZipArchiveEntry sheetEntry = null; - if (sheetName != null) + SetWorkbookRels(_archive.entries); + var sheetRecord = _sheetRecords.SingleOrDefault(s => s.Name == sheetName); + if (sheetRecord == null) { - SetWorkbookRels(_archive.entries); - var sheetRecord = _sheetRecords.SingleOrDefault(s => s.Name == sheetName); - if (sheetRecord == null) - { - if (_config.DynamicSheets == null) - throw new InvalidOperationException("Please check that parameters sheetName/Index are correct"); + if (_config.DynamicSheets == null) + throw new InvalidOperationException("Please check that parameters sheetName/Index are correct"); - var sheetConfig = _config.DynamicSheets.FirstOrDefault(ds => ds.Key == sheetName); - if (sheetConfig != null) - { - sheetRecord = _sheetRecords.SingleOrDefault(s => s.Name == sheetConfig.Name); - } + var sheetConfig = _config.DynamicSheets.FirstOrDefault(ds => ds.Key == sheetName); + if (sheetConfig != null) + { + sheetRecord = _sheetRecords.SingleOrDefault(s => s.Name == sheetConfig.Name); } - sheetEntry = sheets.Single(w => w.FullName == $"xl/{sheetRecord.Path}" || w.FullName == $"/xl/{sheetRecord.Path}" || w.FullName == sheetRecord.Path || sheetRecord.Path == $"/{w.FullName}"); } - else if (sheets.Length > 1) - { - SetWorkbookRels(_archive.entries); - var s = _sheetRecords[0]; - sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}" || w.FullName.TrimStart('/') == s.Path.TrimStart('/')); - } - else - sheetEntry = sheets.Single(); - - return sheetEntry; + sheetEntry = sheets.Single(w => w.FullName == $"xl/{sheetRecord.Path}" || w.FullName == $"/xl/{sheetRecord.Path}" || w.FullName == sheetRecord.Path || sheetRecord.Path == $"/{w.FullName}"); } - - private static IDictionary GetCell(bool useHeaderRow, int maxColumnIndex, Dictionary headRows, int startColumnIndex) + else if (sheets.Length > 1) { - return useHeaderRow ? CustomPropertyHelper.GetEmptyExpandoObject(headRows) : CustomPropertyHelper.GetEmptyExpandoObject(maxColumnIndex, startColumnIndex); + SetWorkbookRels(_archive.entries); + var s = _sheetRecords[0]; + sheetEntry = sheets.Single(w => w.FullName == $"xl/{s.Path}" || w.FullName == $"/xl/{s.Path}" || w.FullName.TrimStart('/') == s.Path.TrimStart('/')); } + else + sheetEntry = sheets.Single(); + + return sheetEntry; + } - private static void SetCellsValueAndHeaders(object cellValue, bool useHeaderRow, Dictionary headRows, bool isFirstRow, IDictionary cell, int columnIndex) + private static IDictionary GetCell(bool useHeaderRow, int maxColumnIndex, Dictionary headRows, int startColumnIndex) + { + return useHeaderRow ? CustomPropertyHelper.GetEmptyExpandoObject(headRows) : CustomPropertyHelper.GetEmptyExpandoObject(maxColumnIndex, startColumnIndex); + } + + private static void SetCellsValueAndHeaders(object cellValue, bool useHeaderRow, Dictionary headRows, bool isFirstRow, IDictionary cell, int columnIndex) + { + if (!useHeaderRow) { - if (!useHeaderRow) - { - //if not using First Head then using A,B,C as index - cell[ColumnHelper.GetAlphabetColumnName(columnIndex)] = cellValue; - return; - } + //if not using First Head then using A,B,C as index + cell[ColumnHelper.GetAlphabetColumnName(columnIndex)] = cellValue; + return; + } - if (isFirstRow) // for startcell logic - { - var cellValueString = cellValue?.ToString(); - if (!string.IsNullOrWhiteSpace(cellValueString)) - headRows.Add(columnIndex, cellValueString); - } - else if (headRows.TryGetValue(columnIndex, out var key)) - { - cell[key] = cellValue; - } + if (isFirstRow) // for startcell logic + { + var cellValueString = cellValue?.ToString(); + if (!string.IsNullOrWhiteSpace(cellValueString)) + headRows.Add(columnIndex, cellValueString); } + else if (headRows.TryGetValue(columnIndex, out var key)) + { + cell[key] = cellValue; + } + } - private void SetSharedStrings() + private void SetSharedStrings() + { + if (_sharedStrings != null) + return; + var sharedStringsEntry = _archive.GetEntry("xl/sharedStrings.xml"); + if (sharedStringsEntry == null) + return; + using (var stream = sharedStringsEntry.Open()) { - if (_sharedStrings != null) - return; - var sharedStringsEntry = _archive.GetEntry("xl/sharedStrings.xml"); - if (sharedStringsEntry == null) - return; - using (var stream = sharedStringsEntry.Open()) + var idx = 0; + if (_config.EnableSharedStringCache && sharedStringsEntry.Length >= _config.SharedStringCacheSize) { - var idx = 0; - if (_config.EnableSharedStringCache && sharedStringsEntry.Length >= _config.SharedStringCacheSize) - { - _sharedStrings = new SharedStringsDiskCache(_config.SharedStringCachePath); - foreach (var sharedString in XmlReaderHelper.GetSharedStrings(stream, _ns)) - _sharedStrings[idx++] = sharedString; - } - else if (_sharedStrings == null) - { - _sharedStrings = XmlReaderHelper.GetSharedStrings(stream, _ns).ToDictionary((x) => idx++, x => x); - } + _sharedStrings = new SharedStringsDiskCache(_config.SharedStringCachePath); + foreach (var sharedString in XmlReaderHelper.GetSharedStrings(stream, _ns)) + _sharedStrings[idx++] = sharedString; + } + else if (_sharedStrings == null) + { + _sharedStrings = XmlReaderHelper.GetSharedStrings(stream, _ns).ToDictionary((x) => idx++, x => x); } } + } - private void SetWorkbookRels(ReadOnlyCollection entries) - { - if (_sheetRecords != null) - return; - _sheetRecords = GetWorkbookRels(entries); - } + private void SetWorkbookRels(ReadOnlyCollection entries) + { + if (_sheetRecords != null) + return; + _sheetRecords = GetWorkbookRels(entries); + } - internal static IEnumerable ReadWorkbook(ReadOnlyCollection entries) + internal static IEnumerable ReadWorkbook(ReadOnlyCollection entries) + { + using (var stream = entries.Single(w => w.FullName == "xl/workbook.xml").Open()) + using (var reader = XmlReader.Create(stream, _xmlSettings)) { - using (var stream = entries.Single(w => w.FullName == "xl/workbook.xml").Open()) - using (var reader = XmlReader.Create(stream, _xmlSettings)) - { - if (!XmlReaderHelper.IsStartElement(reader, "workbook", _ns)) - yield break; + if (!XmlReaderHelper.IsStartElement(reader, "workbook", _ns)) + yield break; - if (!XmlReaderHelper.ReadFirstContent(reader)) - yield break; + if (!XmlReaderHelper.ReadFirstContent(reader)) + yield break; - var activeSheetIndex = 0; - while (!reader.EOF) + var activeSheetIndex = 0; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "bookViews", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "bookViews", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - while (!reader.EOF) + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "workbookView", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "workbookView", _ns)) - { - var activeSheet = reader.GetAttribute("activeTab"); - if (int.TryParse(activeSheet, out var index)) - { - activeSheetIndex = index; - } - - reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(reader)) + var activeSheet = reader.GetAttribute("activeTab"); + if (int.TryParse(activeSheet, out var index)) { - break; + activeSheetIndex = index; } - } - } - else if (XmlReaderHelper.IsStartElement(reader, "sheets", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; - var sheetCount = 0; - while (!reader.EOF) + reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(reader)) { - if (XmlReaderHelper.IsStartElement(reader, "sheet", _ns)) - { - yield return new SheetRecord( - reader.GetAttribute("name"), - reader.GetAttribute("state"), - uint.Parse(reader.GetAttribute("sheetId")), - XmlReaderHelper.GetAttribute(reader, "id", _relationshiopNs), - sheetCount == activeSheetIndex - ); - sheetCount++; - reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; - } + break; } } - else if (!XmlReaderHelper.SkipContent(reader)) - { - yield break; - } } - } - } - - internal List GetWorkbookRels(ReadOnlyCollection entries) - { - var sheetRecords = ReadWorkbook(entries).ToList(); - - using (var stream = entries.Single(w => w.FullName == "xl/_rels/workbook.xml.rels").Open()) - using (var reader = XmlReader.Create(stream, _xmlSettings)) - { - if (!XmlReaderHelper.IsStartElement(reader, "Relationships", "http://schemas.openxmlformats.org/package/2006/relationships")) - return null; - - if (!XmlReaderHelper.ReadFirstContent(reader)) - return null; - - while (!reader.EOF) + else if (XmlReaderHelper.IsStartElement(reader, "sheets", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "Relationship", "http://schemas.openxmlformats.org/package/2006/relationships")) + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; + + var sheetCount = 0; + while (!reader.EOF) { - var rid = reader.GetAttribute("Id"); - foreach (var sheet in sheetRecords) + if (XmlReaderHelper.IsStartElement(reader, "sheet", _ns)) { - if (sheet.Rid == rid) - { - sheet.Path = reader.GetAttribute("Target"); - break; - } + yield return new SheetRecord( + reader.GetAttribute("name"), + reader.GetAttribute("state"), + uint.Parse(reader.GetAttribute("sheetId")), + XmlReaderHelper.GetAttribute(reader, "id", _relationshiopNs), + sheetCount == activeSheetIndex + ); + sheetCount++; + reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; } - - reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; } } + else if (!XmlReaderHelper.SkipContent(reader)) + { + yield break; + } } - - return sheetRecords; } + } - internal class CellAndColumn - { - public object CellValue { get; } - public int ColumnIndex { get; } = -1; - - public CellAndColumn(object cellValue, int columnIndex) - { - CellValue = cellValue; - ColumnIndex = columnIndex; - } - } + internal List GetWorkbookRels(ReadOnlyCollection entries) + { + var sheetRecords = ReadWorkbook(entries).ToList(); - private CellAndColumn ReadCellAndSetColumnIndex(XmlReader reader, int columnIndex, bool withoutCR, int startColumnIndex, string aR, string aT) + using (var stream = entries.Single(w => w.FullName == "xl/_rels/workbook.xml.rels").Open()) + using (var reader = XmlReader.Create(stream, _xmlSettings)) { - const int xfIndex = -1; - int newColumnIndex; - - if (withoutCR) - newColumnIndex = columnIndex + 1; - - //TODO:need to check only need nextColumnIndex or columnIndex - else if (ReferenceHelper.ParseReference(aR, out int referenceColumn, out _)) - newColumnIndex = referenceColumn - 1; // ParseReference is 1-based - else - newColumnIndex = columnIndex; - - columnIndex = newColumnIndex; - - if (columnIndex < startColumnIndex) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - return new CellAndColumn(null, columnIndex); - - while (!reader.EOF) - if (!XmlReaderHelper.SkipContent(reader)) - break; - - return new CellAndColumn(null, columnIndex); - } + if (!XmlReaderHelper.IsStartElement(reader, "Relationships", "http://schemas.openxmlformats.org/package/2006/relationships")) + return null; if (!XmlReaderHelper.ReadFirstContent(reader)) - return new CellAndColumn(null, columnIndex); + return null; - object value = null; while (!reader.EOF) - { - if (XmlReaderHelper.IsStartElement(reader, "v", _ns)) - { - var rawValue = reader.ReadElementContentAsString(); - if (!string.IsNullOrEmpty(rawValue)) - ConvertCellValue(rawValue, aT, xfIndex, out value); - } - else if (XmlReaderHelper.IsStartElement(reader, "is", _ns)) + { + if (XmlReaderHelper.IsStartElement(reader, "Relationship", "http://schemas.openxmlformats.org/package/2006/relationships")) { - var rawValue = StringHelper.ReadStringItem(reader); - if (!string.IsNullOrEmpty(rawValue)) - ConvertCellValue(rawValue, aT, xfIndex, out value); + var rid = reader.GetAttribute("Id"); + foreach (var sheet in sheetRecords) + { + if (sheet.Rid == rid) + { + sheet.Path = reader.GetAttribute("Target"); + break; + } + } + + reader.Skip(); } else if (!XmlReaderHelper.SkipContent(reader)) { break; } } - - return new CellAndColumn(value, columnIndex); } - private void ConvertCellValue(string rawValue, string aT, int xfIndex, out object value) - { - const NumberStyles style = NumberStyles.Any; - var invariantCulture = CultureInfo.InvariantCulture; + return sheetRecords; + } - switch (aT) - { - case "s": - if (int.TryParse(rawValue, style, invariantCulture, out var sstIndex)) - { - if (sstIndex >= 0 && sstIndex < _sharedStrings.Count) - { - //value = Helpers.ConvertEscapeChars(_SharedStrings[sstIndex]); - value = XmlEncoder.DecodeString(_sharedStrings[sstIndex]); - return; - } - } - value = null; - return; + internal class CellAndColumn + { + public object CellValue { get; } + public int ColumnIndex { get; } = -1; - case "inlineStr": - case "str": - //TODO: it will unbox,box - var v = XmlEncoder.DecodeString(rawValue); - if (_config.EnableConvertByteArray) - { - //if str start with "data:image/png;base64," then convert to byte[] https://github.com/mini-software/MiniExcel/issues/318 - if (v != null && v.StartsWith("@@@fileid@@@,", StringComparison.Ordinal)) - { - var path = v.Substring(13); - var entry = _archive.GetEntry(path); - var bytes = new byte[entry.Length]; + public CellAndColumn(object cellValue, int columnIndex) + { + CellValue = cellValue; + ColumnIndex = columnIndex; + } + } - using (var stream = entry.Open()) - using (var ms = new MemoryStream(bytes)) - { - stream.CopyTo(ms); - } - value = bytes; - } - else - { - value = v; - } - } - else - { - value = v; - } - return; + private CellAndColumn ReadCellAndSetColumnIndex(XmlReader reader, int columnIndex, bool withoutCR, int startColumnIndex, string aR, string aT) + { + const int xfIndex = -1; + int newColumnIndex; - case "b": - value = rawValue == "1"; - return; + if (withoutCR) + newColumnIndex = columnIndex + 1; - case "d": - if (DateTime.TryParseExact(rawValue, "yyyy-MM-dd", invariantCulture, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite, out var date)) - { - value = date; - return; - } + //TODO:need to check only need nextColumnIndex or columnIndex + else if (ReferenceHelper.ParseReference(aR, out int referenceColumn, out _)) + newColumnIndex = referenceColumn - 1; // ParseReference is 1-based + else + newColumnIndex = columnIndex; - value = rawValue; - return; + columnIndex = newColumnIndex; - case "e": - value = rawValue; - return; + if (columnIndex < startColumnIndex) + { + if (!XmlReaderHelper.ReadFirstContent(reader)) + return new CellAndColumn(null, columnIndex); - default: - if (double.TryParse(rawValue, style, invariantCulture, out var n)) - { - value = n; - return; - } + while (!reader.EOF) + if (!XmlReaderHelper.SkipContent(reader)) + break; - value = rawValue; - return; - } + return new CellAndColumn(null, columnIndex); } - internal IList GetDimensions() - { - var ranges = new List(); - - var sheets = _archive.entries.Where(e => - e.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase) || - e.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)); + if (!XmlReaderHelper.ReadFirstContent(reader)) + return new CellAndColumn(null, columnIndex); - foreach (var sheet in sheets) + object value = null; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "v", _ns)) + { + var rawValue = reader.ReadElementContentAsString(); + if (!string.IsNullOrEmpty(rawValue)) + ConvertCellValue(rawValue, aT, xfIndex, out value); + } + else if (XmlReaderHelper.IsStartElement(reader, "is", _ns)) { - var maxRowIndex = -1; - var maxColumnIndex = -1; + var rawValue = StringHelper.ReadStringItem(reader); + if (!string.IsNullOrEmpty(rawValue)) + ConvertCellValue(rawValue, aT, xfIndex, out value); + } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; + } + } - string startCell = null; - string endCell = null; + return new CellAndColumn(value, columnIndex); + } - var withoutCR = false; + private void ConvertCellValue(string rawValue, string aT, int xfIndex, out object value) + { + const NumberStyles style = NumberStyles.Any; + var invariantCulture = CultureInfo.InvariantCulture; - using (var sheetStream = sheet.Open()) - using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) + switch (aT) + { + case "s": + if (int.TryParse(rawValue, style, invariantCulture, out var sstIndex)) { - while (reader.Read()) + if (sstIndex >= 0 && sstIndex < _sharedStrings.Count) { - if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) - { - var r = reader.GetAttribute("r"); - if (r != null) - { - if (ReferenceHelper.ParseReference(r, out var column, out var row)) - { - column--; - row--; - maxRowIndex = Math.Max(maxRowIndex, row); - maxColumnIndex = Math.Max(maxColumnIndex, column); - } - } - else - { - withoutCR = true; - break; - } - } - - else if (XmlReaderHelper.IsStartElement(reader, "dimension", _ns)) - { - var refAttr = reader.GetAttribute("ref"); - if (string.IsNullOrEmpty(refAttr)) - throw new InvalidDataException("No dimension data found for the sheet"); - - var rs = refAttr.Split(':'); - if (!ReferenceHelper.ParseReference(rs.Length == 2 ? rs[1] : rs[0], out var col, out var row)) - throw new InvalidDataException("The dimensions of the sheet are invalid"); - - maxColumnIndex = col; - maxRowIndex = row; - - startCell = rs[0]; - endCell = rs[1]; - - break; - } + //value = Helpers.ConvertEscapeChars(_SharedStrings[sstIndex]); + value = XmlEncoder.DecodeString(_sharedStrings[sstIndex]); + return; } } + value = null; + return; - if (withoutCR) + case "inlineStr": + case "str": + //TODO: it will unbox,box + var v = XmlEncoder.DecodeString(rawValue); + if (_config.EnableConvertByteArray) { - using (var sheetStream = sheet.Open()) - using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) + //if str start with "data:image/png;base64," then convert to byte[] https://github.com/mini-software/MiniExcel/issues/318 + if (v != null && v.StartsWith("@@@fileid@@@,", StringComparison.Ordinal)) { - if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) - throw new InvalidDataException("No worksheet data found for the sheet"); - - if (!XmlReaderHelper.ReadFirstContent(reader)) - throw new InvalidOperationException("Excel sheet does not contain any data"); + var path = v.Substring(13); + var entry = _archive.GetEntry(path); + var bytes = new byte[entry.Length]; - while (!reader.EOF) + using (var stream = entry.Open()) + using (var ms = new MemoryStream(bytes)) { - if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; - - while (!reader.EOF) - { - if (XmlReaderHelper.IsStartElement(reader, "row", _ns)) - { - maxRowIndex++; - - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; - - var cellIndex = -1; - while (!reader.EOF) - { - if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) - { - cellIndex++; - maxColumnIndex = Math.Max(maxColumnIndex, cellIndex); - } - - if (!XmlReaderHelper.SkipContent(reader)) - break; - } - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; - } - } - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; - } + stream.CopyTo(ms); } + value = bytes; + } + else + { + value = v; } } + else + { + value = v; + } + return; - var range = new ExcelRange(maxRowIndex, maxColumnIndex) + case "b": + value = rawValue == "1"; + return; + + case "d": + if (DateTime.TryParseExact(rawValue, "yyyy-MM-dd", invariantCulture, DateTimeStyles.AllowLeadingWhite | DateTimeStyles.AllowTrailingWhite, out var date)) { - StartCell = startCell, - EndCell = endCell - }; - ranges.Add(range); - } + value = date; + return; + } + + value = rawValue; + return; + + case "e": + value = rawValue; + return; + + default: + if (double.TryParse(rawValue, style, invariantCulture, out var n)) + { + value = n; + return; + } - return ranges; + value = rawValue; + return; } + } + + internal IList GetDimensions() + { + var ranges = new List(); + + var sheets = _archive.entries.Where(e => + e.FullName.StartsWith("xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase) || + e.FullName.StartsWith("/xl/worksheets/sheet", StringComparison.OrdinalIgnoreCase)); - internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out bool withoutCR, out int maxRowIndex, out int maxColumnIndex) + foreach (var sheet in sheets) { - withoutCR = false; - maxRowIndex = -1; - maxColumnIndex = -1; - using (var sheetStream = sheetEntry.Open()) + var maxRowIndex = -1; + var maxColumnIndex = -1; + + string startCell = null; + string endCell = null; + + var withoutCR = false; + + using (var sheetStream = sheet.Open()) using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) { while (reader.Read()) @@ -883,7 +760,7 @@ internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out boo break; } } - //this method logic depends on dimension to get maxcolumnIndex, if without dimension then it need to foreach all rows first time to get maxColumn and maxRowColumn + else if (XmlReaderHelper.IsStartElement(reader, "dimension", _ns)) { var refAttr = reader.GetAttribute("ref"); @@ -891,13 +768,15 @@ internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out boo throw new InvalidDataException("No dimension data found for the sheet"); var rs = refAttr.Split(':'); - - // issue : https://github.com/mini-software/MiniExcel/issues/102 - if (!ReferenceHelper.ParseReference(rs.Length == 2 ? rs[1] : rs[0], out int cIndex, out int rIndex)) + if (!ReferenceHelper.ParseReference(rs.Length == 2 ? rs[1] : rs[0], out var col, out var row)) throw new InvalidDataException("The dimensions of the sheet are invalid"); - maxRowIndex = rIndex - 1; - maxColumnIndex = cIndex - 1; + maxColumnIndex = col; + maxRowIndex = row; + + startCell = rs[0]; + endCell = rs[1]; + break; } } @@ -905,14 +784,14 @@ internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out boo if (withoutCR) { - using (var sheetStream = sheetEntry.Open()) + using (var sheetStream = sheet.Open()) using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) { if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) - return false; + throw new InvalidDataException("No worksheet data found for the sheet"); if (!XmlReaderHelper.ReadFirstContent(reader)) - return false; + throw new InvalidOperationException("Excel sheet does not contain any data"); while (!reader.EOF) { @@ -930,7 +809,6 @@ internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out boo if (!XmlReaderHelper.ReadFirstContent(reader)) continue; - // Cells var cellIndex = -1; while (!reader.EOF) { @@ -958,90 +836,204 @@ internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out boo } } - return true; + var range = new ExcelRange(maxRowIndex, maxColumnIndex) + { + StartCell = startCell, + EndCell = endCell + }; + ranges.Add(range); } - internal static bool TryGetMergeCells(ZipArchiveEntry sheetEntry, out MergeCells mergeCells) + return ranges; + } + + internal static bool TryGetMaxRowColumnIndex(ZipArchiveEntry sheetEntry, out bool withoutCR, out int maxRowIndex, out int maxColumnIndex) + { + withoutCR = false; + maxRowIndex = -1; + maxColumnIndex = -1; + using (var sheetStream = sheetEntry.Open()) + using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) { - mergeCells = new MergeCells(); - using (var sheetStream = sheetEntry.Open()) - using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings)) + while (reader.Read()) { - if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) - return false; - while (reader.Read()) + if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) { - if (!XmlReaderHelper.IsStartElement(reader, "mergeCells", _ns)) + var r = reader.GetAttribute("r"); + if (r != null) { - continue; + if (ReferenceHelper.ParseReference(r, out var column, out var row)) + { + column--; + row--; + maxRowIndex = Math.Max(maxRowIndex, row); + maxColumnIndex = Math.Max(maxColumnIndex, column); + } + } + else + { + withoutCR = true; + break; } + } + //this method logic depends on dimension to get maxcolumnIndex, if without dimension then it need to foreach all rows first time to get maxColumn and maxRowColumn + else if (XmlReaderHelper.IsStartElement(reader, "dimension", _ns)) + { + var refAttr = reader.GetAttribute("ref"); + if (string.IsNullOrEmpty(refAttr)) + throw new InvalidDataException("No dimension data found for the sheet"); - if (!XmlReaderHelper.ReadFirstContent(reader)) - return false; + var rs = refAttr.Split(':'); - while (!reader.EOF) - { - if (XmlReaderHelper.IsStartElement(reader, "mergeCell", _ns)) - { - var refAttr = reader.GetAttribute("ref"); - var refs = refAttr.Split(':'); - if (refs.Length == 1) - continue; + // issue : https://github.com/mini-software/MiniExcel/issues/102 + if (!ReferenceHelper.ParseReference(rs.Length == 2 ? rs[1] : rs[0], out int cIndex, out int rIndex)) + throw new InvalidDataException("The dimensions of the sheet are invalid"); + + maxRowIndex = rIndex - 1; + maxColumnIndex = cIndex - 1; + break; + } + } + } + + if (withoutCR) + { + using (var sheetStream = sheetEntry.Open()) + using (var reader = XmlReader.Create(sheetStream, _xmlSettings)) + { + if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) + return false; - ReferenceHelper.ParseReference(refs[0], out var x1, out var y1); - ReferenceHelper.ParseReference(refs[1], out var x2, out var y2); + if (!XmlReaderHelper.ReadFirstContent(reader)) + return false; - mergeCells.MergesValues.Add(refs[0], null); + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "sheetData", _ns)) + { + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - // foreach range - var isFirst = true; - for (int x = x1; x <= x2; x++) + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "row", _ns)) { - for (int y = y1; y <= y2; y++) + maxRowIndex++; + + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; + + // Cells + var cellIndex = -1; + while (!reader.EOF) { - if (!isFirst) - mergeCells.MergesMap.Add(ReferenceHelper.ConvertXyToCell(x, y), refs[0]); - isFirst = false; + if (XmlReaderHelper.IsStartElement(reader, "c", _ns)) + { + cellIndex++; + maxColumnIndex = Math.Max(maxColumnIndex, cellIndex); + } + + if (!XmlReaderHelper.SkipContent(reader)) + break; } } - - XmlReaderHelper.SkipContent(reader); - } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; + } } } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; + } } - return true; } } - ~ExcelOpenXmlSheetReader() - { - Dispose(false); - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } + return true; + } - protected virtual void Dispose(bool disposing) + internal static bool TryGetMergeCells(ZipArchiveEntry sheetEntry, out MergeCells mergeCells) + { + mergeCells = new MergeCells(); + using (var sheetStream = sheetEntry.Open()) + using (XmlReader reader = XmlReader.Create(sheetStream, _xmlSettings)) { - if (!_disposed) + if (!XmlReaderHelper.IsStartElement(reader, "worksheet", _ns)) + return false; + while (reader.Read()) { - if (disposing) + if (!XmlReaderHelper.IsStartElement(reader, "mergeCells", _ns)) + { + continue; + } + + if (!XmlReaderHelper.ReadFirstContent(reader)) + return false; + + while (!reader.EOF) { - if (_sharedStrings is SharedStringsDiskCache cache) + if (XmlReaderHelper.IsStartElement(reader, "mergeCell", _ns)) + { + var refAttr = reader.GetAttribute("ref"); + var refs = refAttr.Split(':'); + if (refs.Length == 1) + continue; + + ReferenceHelper.ParseReference(refs[0], out var x1, out var y1); + ReferenceHelper.ParseReference(refs[1], out var x2, out var y2); + + mergeCells.MergesValues.Add(refs[0], null); + + // foreach range + var isFirst = true; + for (int x = x1; x <= x2; x++) + { + for (int y = y1; y <= y2; y++) + { + if (!isFirst) + mergeCells.MergesMap.Add(ReferenceHelper.ConvertXyToCell(x, y), refs[0]); + isFirst = false; + } + } + + XmlReaderHelper.SkipContent(reader); + } + else if (!XmlReaderHelper.SkipContent(reader)) { - cache.Dispose(); + break; } } + } + return true; + } + } + + ~ExcelOpenXmlSheetReader() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } - _disposed = true; + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + if (_sharedStrings is SharedStringsDiskCache cache) + { + cache.Dispose(); + } } + + _disposed = true; } } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs index c7d3bc11..6a143cc7 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.Async.cs @@ -4,180 +4,175 @@ using MiniExcelLibs.Utils; using MiniExcelLibs.WriteAdapter; using MiniExcelLibs.Zip; -using System; -using System.Collections.Generic; using System.IO.Compression; -using System.Linq; using System.Text; -using System.Threading; -using System.Threading.Tasks; using System.Xml.Linq; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal partial class ExcelOpenXmlSheetWriter : IExcelWriter { - internal partial class ExcelOpenXmlSheetWriter : IExcelWriter + public async Task SaveAsAsync(CancellationToken cancellationToken = default) { - public async Task SaveAsAsync(CancellationToken cancellationToken = default) + try { - try - { - await GenerateDefaultOpenXmlAsync(cancellationToken); + await GenerateDefaultOpenXmlAsync(cancellationToken); - var sheets = GetSheets(); - var rowsWritten = new List(); + var sheets = GetSheets(); + var rowsWritten = new List(); - foreach (var sheet in sheets) - { - cancellationToken.ThrowIfCancellationRequested(); - - _sheets.Add(sheet.Item1); //TODO:remove - _currentSheetIndex = sheet.Item1.SheetIdx; - var rows = await CreateSheetXmlAsync(sheet.Item2, sheet.Item1.Path, cancellationToken); - rowsWritten.Add(rows); - } - - await GenerateEndXmlAsync(cancellationToken); - return rowsWritten.ToArray(); - } - finally + foreach (var sheet in sheets) { - _archive.Dispose(); + cancellationToken.ThrowIfCancellationRequested(); + + _sheets.Add(sheet.Item1); //TODO:remove + _currentSheetIndex = sheet.Item1.SheetIdx; + var rows = await CreateSheetXmlAsync(sheet.Item2, sheet.Item1.Path, cancellationToken); + rowsWritten.Add(rows); } + + await GenerateEndXmlAsync(cancellationToken); + return rowsWritten.ToArray(); + } + finally + { + _archive.Dispose(); } + } - public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + public async Task InsertAsync(bool overwriteSheet = false, CancellationToken cancellationToken = default) + { + try { - try - { - if (!_configuration.FastMode) - throw new InvalidOperationException("Insert requires fast mode to be enabled"); + if (!_configuration.FastMode) + throw new InvalidOperationException("Insert requires fast mode to be enabled"); - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - var sheetRecords = new ExcelOpenXmlSheetReader(_stream, _configuration).GetWorkbookRels(_archive.Entries).ToArray(); - foreach (var sheetRecord in sheetRecords.OrderBy(o => o.Id)) - { - cancellationToken.ThrowIfCancellationRequested(); - _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"); + var sheetRecords = new ExcelOpenXmlSheetReader(_stream, _configuration).GetWorkbookRels(_archive.Entries).ToArray(); + foreach (var sheetRecord in sheetRecords.OrderBy(o => o.Id)) + { + cancellationToken.ThrowIfCancellationRequested(); + _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之后,避免不必要的样式更改 + await GenerateStylesXmlAsync(cancellationToken);//GenerateStylesXml必须在校验overwriteSheet之后,避免不必要的样式更改 - int rowsWritten; - if (existSheetDto == null) - { - _currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; - var insertSheetInfo = GetSheetInfos(_defaultSheetName); - var insertSheetDto = insertSheetInfo.ToDto(_currentSheetIndex); - _sheets.Add(insertSheetDto); - rowsWritten = await CreateSheetXmlAsync(_value, insertSheetDto.Path, cancellationToken); - } - else - { - _currentSheetIndex = existSheetDto.SheetIdx; - _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); - rowsWritten = await CreateSheetXmlAsync(_value, existSheetDto.Path, cancellationToken); - } + int rowsWritten; + if (existSheetDto == null) + { + _currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; + var insertSheetInfo = GetSheetInfos(_defaultSheetName); + var insertSheetDto = insertSheetInfo.ToDto(_currentSheetIndex); + _sheets.Add(insertSheetDto); + rowsWritten = await CreateSheetXmlAsync(_value, insertSheetDto.Path, cancellationToken); + } + else + { + _currentSheetIndex = existSheetDto.SheetIdx; + _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); + rowsWritten = await CreateSheetXmlAsync(_value, existSheetDto.Path, cancellationToken); + } - await AddFilesToZipAsync(cancellationToken); + await AddFilesToZipAsync(cancellationToken); - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(_currentSheetIndex - 1))?.Delete(); - await GenerateDrawinRelXmlAsync(_currentSheetIndex - 1, cancellationToken); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(_currentSheetIndex - 1))?.Delete(); + await GenerateDrawinRelXmlAsync(_currentSheetIndex - 1, cancellationToken); - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(_currentSheetIndex - 1))?.Delete(); - await GenerateDrawingXmlAsync(_currentSheetIndex - 1, cancellationToken); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(_currentSheetIndex - 1))?.Delete(); + await GenerateDrawingXmlAsync(_currentSheetIndex - 1, 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); - } + 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.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.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.WorkbookRels)?.Delete(); + await CreateZipEntryAsync(ExcelFileNames.WorkbookRels, null, ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()), cancellationToken); - await InsertContentTypesXmlAsync(cancellationToken); + await InsertContentTypesXmlAsync(cancellationToken); - return rowsWritten; - } - finally - { - _archive.Dispose(); - } + return rowsWritten; } - - internal async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationToken) + finally { - await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken); - await CreateZipEntryAsync(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString, cancellationToken); - await GenerateStylesXmlAsync(cancellationToken); + _archive.Dispose(); } + } - private async Task CreateSheetXmlAsync(object values, string sheetPath, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + internal async Task GenerateDefaultOpenXmlAsync(CancellationToken cancellationToken) + { + await CreateZipEntryAsync(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels, cancellationToken); + await CreateZipEntryAsync(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString, cancellationToken); + await GenerateStylesXmlAsync(cancellationToken); + } + + private async Task CreateSheetXmlAsync(object values, string sheetPath, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - var entry = _archive.CreateEntry(sheetPath, CompressionLevel.Fastest); - var rowsWritten = 0; + var entry = _archive.CreateEntry(sheetPath, CompressionLevel.Fastest); + var rowsWritten = 0; - using (var zipStream = entry.Open()) - using (var writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken)) + using (var zipStream = entry.Open()) + using (var writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken)) + { + if (values == null) { - if (values == null) - { - await WriteEmptySheetAsync(writer); - } - else - { - rowsWritten = await WriteValuesAsync(writer, values, cancellationToken); - } + await WriteEmptySheetAsync(writer); + } + else + { + rowsWritten = await WriteValuesAsync(writer, values, cancellationToken); } - _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); - return rowsWritten; } + _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); + return rowsWritten; + } - private static async Task WriteEmptySheetAsync(MiniExcelAsyncStreamWriter writer) - { - await writer.WriteAsync(ExcelXml.EmptySheetXml); - } + private static async Task WriteEmptySheetAsync(MiniExcelAsyncStreamWriter writer) + { + await writer.WriteAsync(ExcelXml.EmptySheetXml); + } - private static async Task WriteDimensionPlaceholderAsync(MiniExcelAsyncStreamWriter writer) - { - var dimensionPlaceholderPostition = await writer.WriteAndFlushAsync(WorksheetXml.StartDimension); - await writer.WriteAsync(WorksheetXml.DimensionPlaceholder); // end of code will be replaced + private static async Task WriteDimensionPlaceholderAsync(MiniExcelAsyncStreamWriter writer) + { + var dimensionPlaceholderPostition = await writer.WriteAndFlushAsync(WorksheetXml.StartDimension); + await writer.WriteAsync(WorksheetXml.DimensionPlaceholder); // end of code will be replaced - return dimensionPlaceholderPostition; - } + return dimensionPlaceholderPostition; + } - private static async Task WriteDimensionAsync(MiniExcelAsyncStreamWriter writer, int maxRowIndex, int maxColumnIndex, long placeholderPosition) - { - // Flush and save position so that we can get back again. - var position = await writer.FlushAsync(); + private static async Task WriteDimensionAsync(MiniExcelAsyncStreamWriter writer, int maxRowIndex, int maxColumnIndex, long placeholderPosition) + { + // Flush and save position so that we can get back again. + var position = await writer.FlushAsync(); - writer.SetPosition(placeholderPosition); - await writer.WriteAndFlushAsync($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}"""); + writer.SetPosition(placeholderPosition); + await writer.WriteAndFlushAsync($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}"""); - writer.SetPosition(position); - } + writer.SetPosition(position); + } - private async Task WriteValuesAsync(MiniExcelAsyncStreamWriter writer, object values, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + private async Task WriteValuesAsync(MiniExcelAsyncStreamWriter writer, object values, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - IMiniExcelWriteAdapter writeAdapter = null; + IMiniExcelWriteAdapter writeAdapter = null; #if NETSTANDARD2_0_OR_GREATER || NET IAsyncMiniExcelWriteAdapter asyncWriteAdapter = null; #endif - try - { + try + { #if NETSTANDARD2_0_OR_GREATER || NET if (!MiniExcelWriteAdapterFactory.TryGetAsyncWriteAdapter(values, _configuration, out asyncWriteAdapter)) { @@ -190,78 +185,78 @@ private async Task WriteValuesAsync(MiniExcelAsyncStreamWriter writer, obje ? writeAdapter?.GetColumns() : await asyncWriteAdapter.GetColumnsAsync(); #else - writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); + writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); - var isKnownCount = writeAdapter.TryGetKnownCount(out var count); - var props = writeAdapter.GetColumns(); + var isKnownCount = writeAdapter.TryGetKnownCount(out var count); + var props = writeAdapter.GetColumns(); #endif - if (props == null) - { - await WriteEmptySheetAsync(writer); - return 0; - } + if (props == null) + { + await WriteEmptySheetAsync(writer); + return 0; + } - var maxColumnIndex = props.Count; - int maxRowIndex; + var maxColumnIndex = props.Count; + int maxRowIndex; - await writer.WriteAsync(WorksheetXml.StartWorksheetWithRelationship); + await writer.WriteAsync(WorksheetXml.StartWorksheetWithRelationship); - long dimensionPlaceholderPostition = 0; + long dimensionPlaceholderPostition = 0; - // We can write the dimensions directly if the row count is known - if (_configuration.FastMode && !isKnownCount) - { - dimensionPlaceholderPostition = await WriteDimensionPlaceholderAsync(writer); - } - else if (isKnownCount) - { - maxRowIndex = count + (_printHeader ? 1 : 0); - await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, props.Count))); - } + // We can write the dimensions directly if the row count is known + if (_configuration.FastMode && !isKnownCount) + { + dimensionPlaceholderPostition = await WriteDimensionPlaceholderAsync(writer); + } + else if (isKnownCount) + { + maxRowIndex = count + (_printHeader ? 1 : 0); + await writer.WriteAsync(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, props.Count))); + } - //sheet view - await writer.WriteAsync(GetSheetViews()); + //sheet view + await writer.WriteAsync(GetSheetViews()); - //cols:width - ExcelWidthCollection widths = null; - long columnWidthsPlaceholderPosition = 0; - if (_configuration.EnableAutoWidth) - { - columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props); - widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth); - } - else - { - await WriteColumnsWidthsAsync(writer, ExcelWidthCollection.FromProps(props), cancellationToken); - } + //cols:width + ExcelWidthCollection widths = null; + long columnWidthsPlaceholderPosition = 0; + if (_configuration.EnableAutoWidth) + { + columnWidthsPlaceholderPosition = await WriteColumnWidthPlaceholdersAsync(writer, props); + widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth); + } + else + { + await WriteColumnsWidthsAsync(writer, ExcelWidthCollection.FromProps(props), cancellationToken); + } - //header - await writer.WriteAsync(WorksheetXml.StartSheetData); - var currentRowIndex = 0; - if (_printHeader) - { - await PrintHeaderAsync(writer, props, cancellationToken); - currentRowIndex++; - } + //header + await writer.WriteAsync(WorksheetXml.StartSheetData); + var currentRowIndex = 0; + if (_printHeader) + { + await PrintHeaderAsync(writer, props, cancellationToken); + currentRowIndex++; + } - if (writeAdapter != null) + if (writeAdapter != null) + { + foreach (var row in writeAdapter.GetRows(props, cancellationToken)) { - foreach (var row in writeAdapter.GetRows(props, cancellationToken)) + cancellationToken.ThrowIfCancellationRequested(); + + await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex)); + foreach (var cellValue in row) { cancellationToken.ThrowIfCancellationRequested(); - - await writer.WriteAsync(WorksheetXml.StartRow(++currentRowIndex)); - foreach (var cellValue in row) - { - cancellationToken.ThrowIfCancellationRequested(); - await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, - cellValue.Prop, widths); - } - - await writer.WriteAsync(WorksheetXml.EndRow); + await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, + cellValue.Prop, widths); } + + await writer.WriteAsync(WorksheetXml.EndRow); } + } #if NETSTANDARD2_0_OR_GREATER || NET else { @@ -282,337 +277,336 @@ await WriteCellAsync(writer, currentRowIndex, cellValue.CellIndex, cellValue.Val } #endif - maxRowIndex = currentRowIndex; - - await writer.WriteAsync(WorksheetXml.Drawing(_currentSheetIndex)); - await writer.WriteAsync(WorksheetXml.EndSheetData); + maxRowIndex = currentRowIndex; - if (_configuration.AutoFilter) - { - await writer.WriteAsync(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex))); - } + await writer.WriteAsync(WorksheetXml.Drawing(_currentSheetIndex)); + await writer.WriteAsync(WorksheetXml.EndSheetData); - await writer.WriteAsync(WorksheetXml.EndWorksheet); + if (_configuration.AutoFilter) + { + await writer.WriteAsync(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex))); + } - if (_configuration.FastMode && dimensionPlaceholderPostition != 0) - { - await WriteDimensionAsync(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition); - } + await writer.WriteAsync(WorksheetXml.EndWorksheet); - if (_configuration.EnableAutoWidth) - { - await OverWriteColumnWidthPlaceholdersAsync(writer, columnWidthsPlaceholderPosition, widths.Columns, - cancellationToken); - } - - var toSubtract = _printHeader ? 1 : 0; - return maxRowIndex - toSubtract; + if (_configuration.FastMode && dimensionPlaceholderPostition != 0) + { + await WriteDimensionAsync(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition); } - finally + + if (_configuration.EnableAutoWidth) { + await OverWriteColumnWidthPlaceholdersAsync(writer, columnWidthsPlaceholderPosition, widths.Columns, + cancellationToken); + } + + var toSubtract = _printHeader ? 1 : 0; + return maxRowIndex - toSubtract; + } + finally + { #if NETSTANDARD2_0_OR_GREATER || NET if (asyncWriteAdapter is IAsyncDisposable asyncDisposable) { await asyncDisposable.DisposeAsync().ConfigureAwait(false); } #endif - } } + } - private static async Task WriteColumnWidthPlaceholdersAsync(MiniExcelAsyncStreamWriter writer, ICollection props) - { - var placeholderPosition = await writer.FlushAsync(); - await writer.WriteWhitespaceAsync(WorksheetXml.GetColumnPlaceholderLength(props.Count)); - return placeholderPosition; - } + private static async Task WriteColumnWidthPlaceholdersAsync(MiniExcelAsyncStreamWriter writer, ICollection props) + { + var placeholderPosition = await writer.FlushAsync(); + await writer.WriteWhitespaceAsync(WorksheetXml.GetColumnPlaceholderLength(props.Count)); + return placeholderPosition; + } - private static async Task OverWriteColumnWidthPlaceholdersAsync(MiniExcelAsyncStreamWriter writer, long placeholderPosition, IEnumerable columnWidths, CancellationToken cancellationToken = default) - { - cancellationToken.ThrowIfCancellationRequested(); + private static async Task OverWriteColumnWidthPlaceholdersAsync(MiniExcelAsyncStreamWriter writer, long placeholderPosition, IEnumerable columnWidths, CancellationToken cancellationToken = default) + { + cancellationToken.ThrowIfCancellationRequested(); - var position = await writer.FlushAsync(); + var position = await writer.FlushAsync(); - writer.SetPosition(placeholderPosition); - await WriteColumnsWidthsAsync(writer, columnWidths, cancellationToken); + writer.SetPosition(placeholderPosition); + await WriteColumnsWidthsAsync(writer, columnWidths, cancellationToken); - await writer.FlushAsync(); - writer.SetPosition(position); - } + await writer.FlushAsync(); + writer.SetPosition(position); + } - private static async Task WriteColumnsWidthsAsync(MiniExcelAsyncStreamWriter writer, IEnumerable columnWidths, CancellationToken cancellationToken = default) + private static async Task WriteColumnsWidthsAsync(MiniExcelAsyncStreamWriter writer, IEnumerable columnWidths, CancellationToken cancellationToken = default) + { + var hasWrittenStart = false; + foreach (var column in columnWidths) { - var hasWrittenStart = false; - foreach (var column in columnWidths) - { - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); - if (!hasWrittenStart) - { - await writer.WriteAsync(WorksheetXml.StartCols); - hasWrittenStart = true; - } - await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width, column.Hidden)); + if (!hasWrittenStart) + { + await writer.WriteAsync(WorksheetXml.StartCols); + hasWrittenStart = true; } + await writer.WriteAsync(WorksheetXml.Column(column.Index, column.Width, column.Hidden)); + } - if (!hasWrittenStart) - return; + if (!hasWrittenStart) + return; - await writer.WriteAsync(WorksheetXml.EndCols); - } + await writer.WriteAsync(WorksheetXml.EndCols); + } - private async Task PrintHeaderAsync(MiniExcelAsyncStreamWriter writer, List props, CancellationToken cancellationToken = default) - { - const int yIndex = 1; - await writer.WriteAsync(WorksheetXml.StartRow(yIndex)); + private async Task PrintHeaderAsync(MiniExcelAsyncStreamWriter writer, List props, CancellationToken cancellationToken = default) + { + const int yIndex = 1; + await writer.WriteAsync(WorksheetXml.StartRow(yIndex)); - var xIndex = 1; - foreach (var p in props) + var xIndex = 1; + foreach (var p in props) + { + //reason : https://github.com/mini-software/MiniExcel/issues/142 + if (p != null) { - //reason : https://github.com/mini-software/MiniExcel/issues/142 - if (p != null) - { - if (p.ExcelIgnore) - continue; + if (p.ExcelIgnore) + continue; - var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex); - await WriteCellAsync(writer, r, columnName: p.ExcelColumnName); - } - xIndex++; + var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex); + await WriteCellAsync(writer, r, columnName: p.ExcelColumnName); } - - await writer.WriteAsync(WorksheetXml.EndRow); + xIndex++; } - private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, string cellReference, string columnName) - { - await writer.WriteAsync(WorksheetXml.Cell(cellReference, "str", GetCellXfId("1"), ExcelOpenXmlUtils.EncodeXML(columnName))); - } + await writer.WriteAsync(WorksheetXml.EndRow); + } + + private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, string cellReference, string 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) + private async Task WriteCellAsync(MiniExcelAsyncStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo p, ExcelWidthCollection widthCollection) + { + if (p?.CustomFormatter != null) { - if (p?.CustomFormatter != null) + try { - try - { - value = p.CustomFormatter(value); - } - catch - { - //ignored - } + value = p.CustomFormatter(value); } - - var columnReference = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, rowIndex); - var valueIsNull = value is null || value is DBNull; - - if (_configuration.EnableWriteNullValueCell && valueIsNull) + catch { - await writer.WriteAsync(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); - return; + //ignored } + } - var tuple = GetCellValue(rowIndex, cellIndex, value, p, valueIsNull); + var columnReference = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, rowIndex); + var valueIsNull = value is null || value is DBNull; - var styleIndex = tuple.Item1; - var dataType = tuple.Item2; - var cellValue = tuple.Item3; - var columnType = p.ExcelColumnType; + if (_configuration.EnableWriteNullValueCell && valueIsNull) + { + await writer.WriteAsync(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); + return; + } - /*Prefix and suffix blank space will lost after SaveAs #294*/ - var preserveSpace = cellValue != null && (cellValue.StartsWith(" ", StringComparison.Ordinal) || - cellValue.EndsWith(" ", StringComparison.Ordinal)); + var tuple = GetCellValue(rowIndex, cellIndex, value, p, valueIsNull); - await writer.WriteAsync(WorksheetXml.Cell(columnReference, dataType, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); - widthCollection?.AdjustWidth(cellIndex, cellValue); - } + var styleIndex = tuple.Item1; + var dataType = tuple.Item2; + var cellValue = tuple.Item3; + var columnType = p.ExcelColumnType; - private async Task GenerateEndXmlAsync(CancellationToken cancellationToken) - { - await AddFilesToZipAsync(cancellationToken); - await GenerateDrawinRelXmlAsync(cancellationToken); - await GenerateDrawingXmlAsync(cancellationToken); - await GenerateWorkbookXmlAsync(cancellationToken); - await GenerateContentTypesXmlAsync(cancellationToken); - } + /*Prefix and suffix blank space will lost after SaveAs #294*/ + var preserveSpace = cellValue != null && (cellValue.StartsWith(" ", StringComparison.Ordinal) || + cellValue.EndsWith(" ", StringComparison.Ordinal)); - private async Task AddFilesToZipAsync(CancellationToken cancellationToken) - { - foreach (var item in _files) - { - cancellationToken.ThrowIfCancellationRequested(); - await CreateZipEntryAsync(item.Path, item.Byte, cancellationToken); - } - } + await writer.WriteAsync(WorksheetXml.Cell(columnReference, dataType, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); + widthCollection?.AdjustWidth(cellIndex, cellValue); + } - private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken) + private async Task GenerateEndXmlAsync(CancellationToken cancellationToken) + { + await AddFilesToZipAsync(cancellationToken); + await GenerateDrawinRelXmlAsync(cancellationToken); + await GenerateDrawingXmlAsync(cancellationToken); + await GenerateWorkbookXmlAsync(cancellationToken); + await GenerateContentTypesXmlAsync(cancellationToken); + } + + private async Task AddFilesToZipAsync(CancellationToken cancellationToken) + { + foreach (var item in _files) { cancellationToken.ThrowIfCancellationRequested(); - - using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) - { - ISheetStyleBuilder builder = null; - switch (_configuration.TableStyles) - { - case TableStyles.None: - builder = new MinimalSheetStyleBuilder(context); - break; - case TableStyles.Default: - builder = new DefaultSheetStyleBuilder(context, _configuration.StyleOptions); - break; - } - var result = await builder.BuildAsync(cancellationToken); - _cellXfIdMap = result.CellXfIdMap; - } + await CreateZipEntryAsync(item.Path, item.Byte, cancellationToken); } + } - private async Task GenerateDrawinRelXmlAsync(CancellationToken cancellationToken) + private async Task GenerateStylesXmlAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) { - for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) + ISheetStyleBuilder builder = null; + switch (_configuration.TableStyles) { - cancellationToken.ThrowIfCancellationRequested(); - await GenerateDrawinRelXmlAsync(sheetIndex, cancellationToken); + case TableStyles.None: + builder = new MinimalSheetStyleBuilder(context); + break; + case TableStyles.Default: + builder = new DefaultSheetStyleBuilder(context, _configuration.StyleOptions); + break; } + var result = await builder.BuildAsync(cancellationToken); + _cellXfIdMap = result.CellXfIdMap; } + } - 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) + private async Task GenerateDrawinRelXmlAsync(CancellationToken cancellationToken) + { + for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) - { - cancellationToken.ThrowIfCancellationRequested(); - await GenerateDrawingXmlAsync(sheetIndex, cancellationToken); - } + cancellationToken.ThrowIfCancellationRequested(); + await GenerateDrawinRelXmlAsync(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); - } + 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 GenerateWorkbookXmlAsync(CancellationToken cancellationToken) + private async Task GenerateDrawingXmlAsync(CancellationToken cancellationToken) + { + for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { cancellationToken.ThrowIfCancellationRequested(); + await GenerateDrawingXmlAsync(sheetIndex, cancellationToken); + } + } - GenerateWorkBookXmls( - out StringBuilder workbookXml, - out StringBuilder workbookRelsXml, - out Dictionary sheetsRelsXml); + 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); + } - foreach (var sheetRelsXml in sheetsRelsXml) - { - await CreateZipEntryAsync( - ExcelFileNames.SheetRels(sheetRelsXml.Key), - null, - ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value), - cancellationToken); - } + private async Task GenerateWorkbookXmlAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - await CreateZipEntryAsync( - ExcelFileNames.Workbook, - ExcelContentTypes.Workbook, - ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()), - cancellationToken); + GenerateWorkBookXmls( + out StringBuilder workbookXml, + out StringBuilder workbookRelsXml, + out Dictionary sheetsRelsXml); + foreach (var sheetRelsXml in sheetsRelsXml) + { await CreateZipEntryAsync( - ExcelFileNames.WorkbookRels, + ExcelFileNames.SheetRels(sheetRelsXml.Key), null, - ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()), - cancellationToken); + ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value), + cancellationToken); } - private async Task GenerateContentTypesXmlAsync(CancellationToken cancellationToken) - { - var contentTypes = GetContentTypesXml(); - await CreateZipEntryAsync(ExcelFileNames.ContentTypes, null, contentTypes, cancellationToken); - } + await CreateZipEntryAsync( + ExcelFileNames.Workbook, + ExcelContentTypes.Workbook, + ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString()), + cancellationToken); + + await CreateZipEntryAsync( + ExcelFileNames.WorkbookRels, + null, + ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString()), + cancellationToken); + } - private async Task InsertContentTypesXmlAsync(CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + private async Task GenerateContentTypesXmlAsync(CancellationToken cancellationToken) + { + var contentTypes = GetContentTypesXml(); + await CreateZipEntryAsync(ExcelFileNames.ContentTypes, null, contentTypes, cancellationToken); + } + + private async Task InsertContentTypesXmlAsync(CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - var contentTypesZipEntry = _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.ContentTypes); - if (contentTypesZipEntry == null) - { - await GenerateContentTypesXmlAsync(cancellationToken); - return; - } + var contentTypesZipEntry = _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.ContentTypes); + if (contentTypesZipEntry == null) + { + await GenerateContentTypesXmlAsync(cancellationToken); + return; + } #if NET5_0_OR_GREATER await using (var stream = contentTypesZipEntry.Open()) #else - using (var stream = contentTypesZipEntry.Open()) + using (var stream = contentTypesZipEntry.Open()) #endif - { - var doc = XDocument.Load(stream); - var ns = doc.Root?.GetDefaultNamespace(); - var typesElement = doc.Descendants(ns + "Types").Single(); + { + var doc = XDocument.Load(stream); + var ns = doc.Root?.GetDefaultNamespace(); + var typesElement = doc.Descendants(ns + "Types").Single(); - var partNames = new HashSet(StringComparer.InvariantCultureIgnoreCase); - foreach (var partName in typesElement.Elements(ns + "Override").Select(s => s.Attribute("PartName").Value)) - { - partNames.Add(partName); - } + var partNames = new HashSet(StringComparer.InvariantCultureIgnoreCase); + foreach (var partName in typesElement.Elements(ns + "Override").Select(s => s.Attribute("PartName").Value)) + { + partNames.Add(partName); + } - foreach (var p in _zipDictionary) - { - cancellationToken.ThrowIfCancellationRequested(); + foreach (var p in _zipDictionary) + { + cancellationToken.ThrowIfCancellationRequested(); - var partName = $"/{p.Key}"; - if (!partNames.Contains(partName)) - { - var newElement = new XElement(ns + "Override", new XAttribute("ContentType", p.Value.ContentType), new XAttribute("PartName", partName)); - typesElement.Add(newElement); - } + var partName = $"/{p.Key}"; + if (!partNames.Contains(partName)) + { + var newElement = new XElement(ns + "Override", new XAttribute("ContentType", p.Value.ContentType), new XAttribute("PartName", partName)); + typesElement.Add(newElement); } - - stream.Position = 0; - doc.Save(stream); } + + stream.Position = 0; + doc.Save(stream); } + } - private async Task CreateZipEntryAsync(string path, string contentType, string content, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + private async Task CreateZipEntryAsync(string path, string contentType, string content, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - var entry = _archive.CreateEntry(path, CompressionLevel.Fastest); + var entry = _archive.CreateEntry(path, CompressionLevel.Fastest); #if NET5_0_OR_GREATER await using (var zipStream = entry.Open()) #else - using (var zipStream = entry.Open()) + using (var zipStream = entry.Open()) #endif - using (var writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken)) - await writer.WriteAsync(content); + using (var writer = new MiniExcelAsyncStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize, cancellationToken)) + await writer.WriteAsync(content); - if (!string.IsNullOrEmpty(contentType)) - _zipDictionary.Add(path, new ZipPackageInfo(entry, contentType)); - } + if (!string.IsNullOrEmpty(contentType)) + _zipDictionary.Add(path, new ZipPackageInfo(entry, contentType)); + } - private async Task CreateZipEntryAsync(string path, byte[] content, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + private async Task CreateZipEntryAsync(string path, byte[] content, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); - var entry = _archive.CreateEntry(path, CompressionLevel.Fastest); + var entry = _archive.CreateEntry(path, CompressionLevel.Fastest); #if NET5_0_OR_GREATER await using (var zipStream = entry.Open()) #else - using (var zipStream = entry.Open()) + using (var zipStream = entry.Open()) #endif - await zipStream.WriteAsync(content, 0, content.Length, cancellationToken); - } + await zipStream.WriteAsync(content, 0, content.Length, cancellationToken); } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs index da5b2ef1..d59c576b 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.DefaultOpenXml.cs @@ -1,430 +1,423 @@ -using MiniExcelLibs.Attributes; using MiniExcelLibs.OpenXml.Constants; using MiniExcelLibs.OpenXml.Models; -using MiniExcelLibs.OpenXml.Styles; using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; -using System; -using System.Collections.Generic; using System.Data; using System.Globalization; -using System.Linq; using System.Text; using static MiniExcelLibs.Utils.ImageHelper; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal partial class ExcelOpenXmlSheetWriter : IExcelWriter { - internal partial class ExcelOpenXmlSheetWriter : IExcelWriter - { - private readonly Dictionary _zipDictionary = new Dictionary(); - private Dictionary _cellXfIdMap; + private readonly Dictionary _zipDictionary = new Dictionary(); + private Dictionary _cellXfIdMap; - private IEnumerable> GetSheets() + private IEnumerable> GetSheets() + { + var sheetId = 0; + if (_value is IDictionary dictionary) { - var sheetId = 0; - if (_value is IDictionary dictionary) + foreach (var sheet in dictionary) { - foreach (var sheet in dictionary) - { - sheetId++; - var sheetInfos = GetSheetInfos(sheet.Key); - if (sheetInfos.ExcelSheetName.Length > 31) - throw new ArgumentException("Sheet names must be less than 31 characters"); - - yield return Tuple.Create(sheetInfos.ToDto(sheetId), sheet.Value); - } - - yield break; - } + sheetId++; + var sheetInfos = GetSheetInfos(sheet.Key); + if (sheetInfos.ExcelSheetName.Length > 31) + throw new ArgumentException("Sheet names must be less than 31 characters"); - if (_value is DataSet dataSet) - { - foreach (DataTable dt in dataSet.Tables) - { - sheetId++; - var sheetInfos = GetSheetInfos(dt.TableName); - yield return Tuple.Create(sheetInfos.ToDto(sheetId), dt); - } - - yield break; + yield return Tuple.Create(sheetInfos.ToDto(sheetId), sheet.Value); } - sheetId++; - var defaultSheetInfo = GetSheetInfos(_defaultSheetName); - yield return Tuple.Create(defaultSheetInfo.ToDto(sheetId), _value); + yield break; } - private ExcellSheetInfo GetSheetInfos(string sheetName) + if (_value is DataSet dataSet) { - var info = new ExcellSheetInfo + foreach (DataTable dt in dataSet.Tables) { - ExcelSheetName = sheetName, - Key = sheetName, - ExcelSheetState = SheetState.Visible - }; + sheetId++; + var sheetInfos = GetSheetInfos(dt.TableName); + yield return Tuple.Create(sheetInfos.ToDto(sheetId), dt); + } - if (_configuration.DynamicSheets == null || _configuration.DynamicSheets.Length <= 0) - return info; + yield break; + } - var dynamicSheet = _configuration.DynamicSheets.SingleOrDefault(_ => _.Key == sheetName); - if (dynamicSheet == null) - return info; + sheetId++; + var defaultSheetInfo = GetSheetInfos(_defaultSheetName); + yield return Tuple.Create(defaultSheetInfo.ToDto(sheetId), _value); + } - info.ExcelSheetState = dynamicSheet.State; - if (dynamicSheet.Name != null) - info.ExcelSheetName = dynamicSheet.Name; + private ExcellSheetInfo GetSheetInfos(string sheetName) + { + var info = new ExcellSheetInfo + { + ExcelSheetName = sheetName, + Key = sheetName, + ExcelSheetState = SheetState.Visible + }; + if (_configuration.DynamicSheets == null || _configuration.DynamicSheets.Length <= 0) return info; - } - private string GetSheetViews() - { - // exit early if no style to write - if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0 && !_configuration.RightToLeft) - return string.Empty; + var dynamicSheet = _configuration.DynamicSheets.SingleOrDefault(_ => _.Key == sheetName); + if (dynamicSheet == null) + return info; - var sb = new StringBuilder(); + info.ExcelSheetState = dynamicSheet.State; + if (dynamicSheet.Name != null) + info.ExcelSheetName = dynamicSheet.Name; - // start sheetViews - sb.Append(WorksheetXml.StartSheetViews); - sb.Append(WorksheetXml.StartSheetView(rightToLeft: _configuration.RightToLeft)); + return info; + } - // Write panes - sb.Append(GetPanes()); + private string GetSheetViews() + { + // exit early if no style to write + if (_configuration.FreezeRowCount <= 0 && _configuration.FreezeColumnCount <= 0 && !_configuration.RightToLeft) + return string.Empty; - // end sheetViews - sb.Append(WorksheetXml.EndSheetView); - sb.Append(WorksheetXml.EndSheetViews); + var sb = new StringBuilder(); - return sb.ToString(); - } + // start sheetViews + sb.Append(WorksheetXml.StartSheetViews); + sb.Append(WorksheetXml.StartSheetView(rightToLeft: _configuration.RightToLeft)); - private string GetPanes() - { - var sb = new StringBuilder(); + // Write panes + sb.Append(GetPanes()); - string activePane; - switch (_configuration.FreezeColumnCount > 0) - { - case true when _configuration.FreezeRowCount > 0: - activePane = "bottomRight"; - break; - case true: - activePane = "topRight"; - break; - default: - activePane = "bottomLeft"; - break; - } - sb.Append( - WorksheetXml.StartPane( - xSplit: _configuration.FreezeColumnCount > 0 ? _configuration.FreezeColumnCount : (int?)null, - ySplit: _configuration.FreezeRowCount > 0 ? _configuration.FreezeRowCount : (int?)null, - topLeftCell: ExcelOpenXmlUtils.ConvertXyToCell( - _configuration.FreezeColumnCount + 1, - _configuration.FreezeRowCount + 1 - ), - activePane: activePane, - state: "frozen" - ) - ); - - // write pane selections - if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0) - { - // freeze row and column - /* - - - - */ - var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, 1); - sb.Append(WorksheetXml.PaneSelection("topRight", cellTR, cellTR)); - - var cellBL = ExcelOpenXmlUtils.ConvertXyToCell(1, _configuration.FreezeRowCount + 1); - sb.Append(WorksheetXml.PaneSelection("bottomLeft", cellBL, cellBL)); - - var cellBR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, _configuration.FreezeRowCount + 1); - sb.Append(WorksheetXml.PaneSelection("bottomRight", cellBR, cellBR)); - } - else if (_configuration.FreezeColumnCount > 0) - { - // freeze column - /* - - */ - var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount, 1); - sb.Append(WorksheetXml.PaneSelection("topRight", cellTR, cellTR)); + // end sheetViews + sb.Append(WorksheetXml.EndSheetView); + sb.Append(WorksheetXml.EndSheetViews); - } - else - { - // freeze row - /* - - */ - sb.Append(WorksheetXml.PaneSelection("bottomLeft", null, null)); + return sb.ToString(); + } - } + private string GetPanes() + { + var sb = new StringBuilder(); - return sb.ToString(); + string activePane; + switch (_configuration.FreezeColumnCount > 0) + { + case true when _configuration.FreezeRowCount > 0: + activePane = "bottomRight"; + break; + case true: + activePane = "topRight"; + break; + default: + activePane = "bottomLeft"; + break; + } + sb.Append( + WorksheetXml.StartPane( + xSplit: _configuration.FreezeColumnCount > 0 ? _configuration.FreezeColumnCount : (int?)null, + ySplit: _configuration.FreezeRowCount > 0 ? _configuration.FreezeRowCount : (int?)null, + topLeftCell: ExcelOpenXmlUtils.ConvertXyToCell( + _configuration.FreezeColumnCount + 1, + _configuration.FreezeRowCount + 1 + ), + activePane: activePane, + state: "frozen" + ) + ); + + // write pane selections + if (_configuration.FreezeColumnCount > 0 && _configuration.FreezeRowCount > 0) + { + // freeze row and column + /* + + + + */ + var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, 1); + sb.Append(WorksheetXml.PaneSelection("topRight", cellTR, cellTR)); + + var cellBL = ExcelOpenXmlUtils.ConvertXyToCell(1, _configuration.FreezeRowCount + 1); + sb.Append(WorksheetXml.PaneSelection("bottomLeft", cellBL, cellBL)); + + var cellBR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount + 1, _configuration.FreezeRowCount + 1); + sb.Append(WorksheetXml.PaneSelection("bottomRight", cellBR, cellBR)); } + else if (_configuration.FreezeColumnCount > 0) + { + // freeze column + /* + + */ + var cellTR = ExcelOpenXmlUtils.ConvertXyToCell(_configuration.FreezeColumnCount, 1); + sb.Append(WorksheetXml.PaneSelection("topRight", cellTR, cellTR)); - private Tuple GetCellValue(int rowIndex, int cellIndex, object value, ExcelColumnInfo columnInfo, bool valueIsNull) + } + else { - if (valueIsNull) - return Tuple.Create("2", "str", string.Empty); + // freeze row + /* + + */ + sb.Append(WorksheetXml.PaneSelection("bottomLeft", null, null)); - if (value is string str) - return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(str)); + } - var type = GetValueType(value, columnInfo); + return sb.ToString(); + } - if (columnInfo?.ExcelFormat != null && columnInfo?.ExcelFormatId == -1 && value is IFormattable formattableValue) - { - var formattedStr = formattableValue.ToString(columnInfo.ExcelFormat, _configuration.Culture); - return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(formattedStr)); - } + private Tuple GetCellValue(int rowIndex, int cellIndex, object value, ExcelColumnInfo columnInfo, bool valueIsNull) + { + if (valueIsNull) + return Tuple.Create("2", "str", string.Empty); - if (type == typeof(DateTime)) - return GetDateTimeValue((DateTime)value, columnInfo); + if (value is string str) + return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(str)); -#if NET6_0_OR_GREATER - if (type == typeof(DateOnly)) - return GetDateTimeValue(((DateOnly)value).ToDateTime(new TimeOnly()), columnInfo); -#endif - if (type.IsEnum) - { - var description = CustomPropertyHelper.DescriptionAttr(type, value); - return Tuple.Create("2", "str", description ?? value.ToString()); - } + var type = GetValueType(value, columnInfo); - if (TypeHelper.IsNumericType(type)) - { - var cellValue = GetNumericValue(value, type); + if (columnInfo?.ExcelFormat != null && columnInfo?.ExcelFormatId == -1 && value is IFormattable formattableValue) + { + var formattedStr = formattableValue.ToString(columnInfo.ExcelFormat, _configuration.Culture); + return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(formattedStr)); + } - if (columnInfo == null || columnInfo.ExcelFormat == null) - { - var dataType = _configuration.Culture == CultureInfo.InvariantCulture ? "n" : "str"; - return Tuple.Create("2", dataType, cellValue); - } + if (type == typeof(DateTime)) + return GetDateTimeValue((DateTime)value, columnInfo); - return Tuple.Create(columnInfo.ExcelFormatId.ToString(), (string)null, cellValue); - } +#if NET6_0_OR_GREATER + if (type == typeof(DateOnly)) + return GetDateTimeValue(((DateOnly)value).ToDateTime(new TimeOnly()), columnInfo); +#endif + if (type.IsEnum) + { + var description = CustomPropertyHelper.DescriptionAttr(type, value); + return Tuple.Create("2", "str", description ?? value.ToString()); + } - if (type == typeof(bool)) - return Tuple.Create("2", "b", (bool)value ? "1" : "0"); + if (TypeHelper.IsNumericType(type)) + { + var cellValue = GetNumericValue(value, type); - if (type == typeof(byte[]) && _configuration.EnableConvertByteArray) + if (columnInfo == null || columnInfo.ExcelFormat == null) { - var base64 = GetFileValue(rowIndex, cellIndex, value); - if (_configuration.EnableWriteFilePath) - { - return Tuple.Create("4", "str", ExcelOpenXmlUtils.EncodeXML(base64)); - } - return Tuple.Create("4", "str", ""); + var dataType = _configuration.Culture == CultureInfo.InvariantCulture ? "n" : "str"; + return Tuple.Create("2", dataType, cellValue); } - return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(value.ToString())); + return Tuple.Create(columnInfo.ExcelFormatId.ToString(), (string)null, cellValue); } - private static Type GetValueType(object value, ExcelColumnInfo columnInfo) + if (type == typeof(bool)) + return Tuple.Create("2", "b", (bool)value ? "1" : "0"); + + if (type == typeof(byte[]) && _configuration.EnableConvertByteArray) { - Type type; - if (columnInfo == null || columnInfo.Key != null) - { - // TODO: need to optimize - // Dictionary need to check type every time, so it's slow.. - type = value.GetType(); - type = Nullable.GetUnderlyingType(type) ?? type; - } - else + var base64 = GetFileValue(rowIndex, cellIndex, value); + if (_configuration.EnableWriteFilePath) { - type = columnInfo.ExcludeNullableType; //sometime it doesn't need to re-get type like prop + return Tuple.Create("4", "str", ExcelOpenXmlUtils.EncodeXML(base64)); } - - return type; + return Tuple.Create("4", "str", ""); } - private string GetNumericValue(object value, Type type) + return Tuple.Create("2", "str", ExcelOpenXmlUtils.EncodeXML(value.ToString())); + } + + private static Type GetValueType(object value, ExcelColumnInfo columnInfo) + { + Type type; + if (columnInfo == null || columnInfo.Key != null) + { + // TODO: need to optimize + // Dictionary need to check type every time, so it's slow.. + type = value.GetType(); + type = Nullable.GetUnderlyingType(type) ?? type; + } + else { - if (type.IsAssignableFrom(typeof(decimal))) - return ((decimal)value).ToString(_configuration.Culture); + type = columnInfo.ExcludeNullableType; //sometime it doesn't need to re-get type like prop + } - if (type.IsAssignableFrom(typeof(int))) - return ((int)value).ToString(_configuration.Culture); + return type; + } - if (type.IsAssignableFrom(typeof(double))) - return ((double)value).ToString(_configuration.Culture); + private string GetNumericValue(object value, Type type) + { + if (type.IsAssignableFrom(typeof(decimal))) + return ((decimal)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(long))) - return ((long)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(int))) + return ((int)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(uint))) - return ((uint)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(double))) + return ((double)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(ushort))) - return ((ushort)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(long))) + return ((long)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(ulong))) - return ((ulong)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(uint))) + return ((uint)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(short))) - return ((short)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(ushort))) + return ((ushort)value).ToString(_configuration.Culture); - if (type.IsAssignableFrom(typeof(float))) - return ((float)value).ToString(_configuration.Culture); + if (type.IsAssignableFrom(typeof(ulong))) + return ((ulong)value).ToString(_configuration.Culture); - return (decimal.Parse(value.ToString())).ToString(_configuration.Culture); - } + if (type.IsAssignableFrom(typeof(short))) + return ((short)value).ToString(_configuration.Culture); - private string GetFileValue(int rowIndex, int cellIndex, object value) - { - var bytes = (byte[])value; - - // TODO: Setting configuration because it might have high cost? - var format = GetImageFormat(bytes); - //it can't insert to zip first to avoid cache image to memory - //because sheet xml is opening.. https://github.com/mini-software/MiniExcel/issues/304#issuecomment-1017031691 - //int rowIndex, int cellIndex - var file = new FileDto() - { - Byte = bytes, - RowIndex = rowIndex, - CellIndex = cellIndex, - SheetId = _currentSheetIndex - }; + if (type.IsAssignableFrom(typeof(float))) + return ((float)value).ToString(_configuration.Culture); - if (format != ImageFormat.unknown) - { - file.Extension = format.ToString(); - file.IsImage = true; - } - else - { - file.Extension = "bin"; - } + return (decimal.Parse(value.ToString())).ToString(_configuration.Culture); + } - _files.Add(file); + private string GetFileValue(int rowIndex, int cellIndex, object value) + { + var bytes = (byte[])value; + + // TODO: Setting configuration because it might have high cost? + var format = GetImageFormat(bytes); + //it can't insert to zip first to avoid cache image to memory + //because sheet xml is opening.. https://github.com/mini-software/MiniExcel/issues/304#issuecomment-1017031691 + //int rowIndex, int cellIndex + var file = new FileDto() + { + Byte = bytes, + RowIndex = rowIndex, + CellIndex = cellIndex, + SheetId = _currentSheetIndex + }; - //TODO:Convert to base64 - var base64 = $"@@@fileid@@@,{file.Path}"; - return base64; + if (format != ImageFormat.unknown) + { + file.Extension = format.ToString(); + file.IsImage = true; } - - private Tuple GetDateTimeValue(DateTime value, ExcelColumnInfo columnInfo) + else { - string cellValue = null; - if (!ReferenceEquals(_configuration.Culture, CultureInfo.InvariantCulture)) - { - cellValue = value.ToString(_configuration.Culture); - return Tuple.Create("2", "str", cellValue); - } + file.Extension = "bin"; + } - var oaDate = CorrectDateTimeValue(value); - cellValue = oaDate.ToString(CultureInfo.InvariantCulture); - var format = columnInfo?.ExcelFormat != null ? columnInfo.ExcelFormatId.ToString() : "3"; + _files.Add(file); - return Tuple.Create(format, (string)null, cellValue); - } + //TODO:Convert to base64 + var base64 = $"@@@fileid@@@,{file.Path}"; + return base64; + } - private static double CorrectDateTimeValue(DateTime value) + private Tuple GetDateTimeValue(DateTime value, ExcelColumnInfo columnInfo) + { + string cellValue = null; + if (!ReferenceEquals(_configuration.Culture, CultureInfo.InvariantCulture)) { - var oaDate = value.ToOADate(); + cellValue = value.ToString(_configuration.Culture); + return Tuple.Create("2", "str", cellValue); + } - // Excel says 1900 was a leap year :( Replicate an incorrect behavior thanks - // to Lotus 1-2-3 decision from 1983... - // https://github.com/ClosedXML/ClosedXML/blob/develop/ClosedXML/Extensions/DateTimeExtensions.cs#L45 - const int nonExistent1900Feb29SerialDate = 60; - if (oaDate <= nonExistent1900Feb29SerialDate) - { - oaDate -= 1; - } + var oaDate = CorrectDateTimeValue(value); + cellValue = oaDate.ToString(CultureInfo.InvariantCulture); + var format = columnInfo?.ExcelFormat != null ? columnInfo.ExcelFormatId.ToString() : "3"; - return oaDate; - } + return Tuple.Create(format, (string)null, cellValue); + } + + private static double CorrectDateTimeValue(DateTime value) + { + var oaDate = value.ToOADate(); - private static string GetDimensionRef(int maxRowIndex, int maxColumnIndex) + // Excel says 1900 was a leap year :( Replicate an incorrect behavior thanks + // to Lotus 1-2-3 decision from 1983... + // https://github.com/ClosedXML/ClosedXML/blob/develop/ClosedXML/Extensions/DateTimeExtensions.cs#L45 + const int nonExistent1900Feb29SerialDate = 60; + if (oaDate <= nonExistent1900Feb29SerialDate) { - string dimensionRef; - if (maxRowIndex == 0 && maxColumnIndex == 0) - dimensionRef = "A1"; - else if (maxRowIndex <= 1 && maxColumnIndex == 0) - dimensionRef = "A1"; - else if (maxColumnIndex <= 1) - dimensionRef = $"A1:A{maxRowIndex}"; - else if (maxRowIndex == 0) - dimensionRef = $"A1:{ColumnHelper.GetAlphabetColumnName(maxColumnIndex - 1)}1"; - else - dimensionRef = $"A1:{ColumnHelper.GetAlphabetColumnName(maxColumnIndex - 1)}{maxRowIndex}"; - return dimensionRef; + oaDate -= 1; } - private string GetDrawingRelationshipXml(int sheetIndex) - { - var drawing = new StringBuilder(); - foreach (var image in _files.Where(w => w.IsImage && w.SheetId == sheetIndex + 1)) - { - drawing.AppendLine(ExcelXml.ImageRelationship(image)); - } + return oaDate; + } - return drawing.ToString(); - } + private static string GetDimensionRef(int maxRowIndex, int maxColumnIndex) + { + string dimensionRef; + if (maxRowIndex == 0 && maxColumnIndex == 0) + dimensionRef = "A1"; + else if (maxRowIndex <= 1 && maxColumnIndex == 0) + dimensionRef = "A1"; + else if (maxColumnIndex <= 1) + dimensionRef = $"A1:A{maxRowIndex}"; + else if (maxRowIndex == 0) + dimensionRef = $"A1:{ColumnHelper.GetAlphabetColumnName(maxColumnIndex - 1)}1"; + else + dimensionRef = $"A1:{ColumnHelper.GetAlphabetColumnName(maxColumnIndex - 1)}{maxRowIndex}"; + return dimensionRef; + } - private string GetDrawingXml(int sheetIndex) + private string GetDrawingRelationshipXml(int sheetIndex) + { + var drawing = new StringBuilder(); + foreach (var image in _files.Where(w => w.IsImage && w.SheetId == sheetIndex + 1)) { - var drawing = new StringBuilder(); + drawing.AppendLine(ExcelXml.ImageRelationship(image)); + } - for (int fileIndex = 0; fileIndex < _files.Count; fileIndex++) - { - var file = _files[fileIndex]; - if (file.IsImage && file.SheetId == sheetIndex + 1) - { - drawing.Append(ExcelXml.DrawingXml(file, fileIndex)); - } - } + return drawing.ToString(); + } - return drawing.ToString(); - } + private string GetDrawingXml(int sheetIndex) + { + var drawing = new StringBuilder(); - private void GenerateWorkBookXmls( - out StringBuilder workbookXml, - out StringBuilder workbookRelsXml, - out Dictionary sheetsRelsXml) + for (int fileIndex = 0; fileIndex < _files.Count; fileIndex++) { - workbookXml = new StringBuilder(); - workbookRelsXml = new StringBuilder(); - sheetsRelsXml = new Dictionary(); - var sheetId = 0; - foreach (var sheetDto in _sheets) + var file = _files[fileIndex]; + if (file.IsImage && file.SheetId == sheetIndex + 1) { - sheetId++; - workbookXml.AppendLine(ExcelXml.Sheet(sheetDto, sheetId)); - - workbookRelsXml.AppendLine(ExcelXml.WorksheetRelationship(sheetDto)); - - //TODO: support multiple drawing - //TODO: ../drawings/drawing1.xml or /xl/drawings/drawing1.xml - sheetsRelsXml.Add(sheetDto.SheetIdx, ExcelXml.DrawingRelationship(sheetId)); + drawing.Append(ExcelXml.DrawingXml(file, fileIndex)); } } - private string GetContentTypesXml() + return drawing.ToString(); + } + + private void GenerateWorkBookXmls( + out StringBuilder workbookXml, + out StringBuilder workbookRelsXml, + out Dictionary sheetsRelsXml) + { + workbookXml = new StringBuilder(); + workbookRelsXml = new StringBuilder(); + sheetsRelsXml = new Dictionary(); + var sheetId = 0; + foreach (var sheetDto in _sheets) { - var sb = new StringBuilder(ExcelXml.StartTypes); - foreach (var p in _zipDictionary) - { - sb.Append(ExcelXml.ContentType(p.Value.ContentType, p.Key)); - } + sheetId++; + workbookXml.AppendLine(ExcelXml.Sheet(sheetDto, sheetId)); + + workbookRelsXml.AppendLine(ExcelXml.WorksheetRelationship(sheetDto)); - sb.Append(ExcelXml.EndTypes); - return sb.ToString(); + //TODO: support multiple drawing + //TODO: ../drawings/drawing1.xml or /xl/drawings/drawing1.xml + sheetsRelsXml.Add(sheetDto.SheetIdx, ExcelXml.DrawingRelationship(sheetId)); } + } - private string GetCellXfId(string styleIndex) + private string GetContentTypesXml() + { + var sb = new StringBuilder(ExcelXml.StartTypes); + foreach (var p in _zipDictionary) { - return _cellXfIdMap.TryGetValue(styleIndex, out var cellXfId) ? cellXfId : styleIndex; + sb.Append(ExcelXml.ContentType(p.Value.ContentType, p.Key)); } + + sb.Append(ExcelXml.EndTypes); + return sb.ToString(); } -} + private string GetCellXfId(string styleIndex) + { + return _cellXfIdMap.TryGetValue(styleIndex, out var cellXfId) ? cellXfId : styleIndex; + } +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs index a0c96e2d..0fc5af3d 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlSheetWriter.cs @@ -5,531 +5,526 @@ using MiniExcelLibs.Utils; using MiniExcelLibs.WriteAdapter; using MiniExcelLibs.Zip; -using System; -using System.Collections.Generic; -using System.IO; using System.IO.Compression; -using System.Linq; using System.Text; using System.Xml.Linq; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal partial class ExcelOpenXmlSheetWriter : IExcelWriter { - internal partial class ExcelOpenXmlSheetWriter : IExcelWriter + private readonly MiniExcelZipArchive _archive; + private static readonly UTF8Encoding _utf8WithBom = new UTF8Encoding(true); + private readonly OpenXmlConfiguration _configuration; + private readonly Stream _stream; + private readonly bool _printHeader; + private readonly object _value; + private readonly string _defaultSheetName; + private readonly List _sheets = new List(); + private readonly List _files = new List(); + private int _currentSheetIndex = 0; + + public ExcelOpenXmlSheetWriter(Stream stream, object value, string sheetName, IConfiguration configuration, bool printHeader) + { + _stream = stream; + // Why ZipArchiveMode.Update not ZipArchiveMode.Create? + // R : Mode create - ZipArchiveEntry does not support seeking.' + _configuration = configuration as OpenXmlConfiguration ?? OpenXmlConfiguration.DefaultConfig; + if (_configuration.EnableAutoWidth && !_configuration.FastMode) + throw new InvalidOperationException("Auto width requires fast mode to be enabled"); + + var archiveMode = _configuration.FastMode ? ZipArchiveMode.Update : ZipArchiveMode.Create; + _archive = new MiniExcelZipArchive(_stream, archiveMode, true, _utf8WithBom); + + _value = value; + _printHeader = printHeader; + _defaultSheetName = sheetName; + } + + public int[] SaveAs() { - private readonly MiniExcelZipArchive _archive; - private static readonly UTF8Encoding _utf8WithBom = new UTF8Encoding(true); - private readonly OpenXmlConfiguration _configuration; - private readonly Stream _stream; - private readonly bool _printHeader; - private readonly object _value; - private readonly string _defaultSheetName; - private readonly List _sheets = new List(); - private readonly List _files = new List(); - private int _currentSheetIndex = 0; + GenerateDefaultOpenXml(); - public ExcelOpenXmlSheetWriter(Stream stream, object value, string sheetName, IConfiguration configuration, bool printHeader) + var sheets = GetSheets(); + var rowsWritten = new List(); + + foreach (var sheet in sheets) { - _stream = stream; - // Why ZipArchiveMode.Update not ZipArchiveMode.Create? - // R : Mode create - ZipArchiveEntry does not support seeking.' - _configuration = configuration as OpenXmlConfiguration ?? OpenXmlConfiguration.DefaultConfig; - if (_configuration.EnableAutoWidth && !_configuration.FastMode) - throw new InvalidOperationException("Auto width requires fast mode to be enabled"); + _sheets.Add(sheet.Item1); //TODO:remove + _currentSheetIndex = sheet.Item1.SheetIdx; + var rows = CreateSheetXml(sheet.Item2, sheet.Item1.Path); + rowsWritten.Add(rows); + } - var archiveMode = _configuration.FastMode ? ZipArchiveMode.Update : ZipArchiveMode.Create; - _archive = new MiniExcelZipArchive(_stream, archiveMode, true, _utf8WithBom); + GenerateEndXml(); + _archive.Dispose(); - _value = value; - _printHeader = printHeader; - _defaultSheetName = sheetName; + return rowsWritten.ToArray(); + } + + public int 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"); } - public int[] SaveAs() + GenerateStylesXml();//GenerateStylesXml必须在校验overwriteSheet之后,避免不必要的样式更改 + int rowsWritten; + if (existSheetDto == null) + { + _currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; + var insertSheetInfo = GetSheetInfos(_defaultSheetName); + var insertSheetDto = insertSheetInfo.ToDto(_currentSheetIndex); + _sheets.Add(insertSheetDto); + rowsWritten = CreateSheetXml(_value, insertSheetDto.Path); + } + else { - GenerateDefaultOpenXml(); + _currentSheetIndex = existSheetDto.SheetIdx; + _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); + rowsWritten = CreateSheetXml(_value, existSheetDto.Path); + } - var sheets = GetSheets(); - var rowsWritten = new List(); - - foreach (var sheet in sheets) - { - _sheets.Add(sheet.Item1); //TODO:remove - _currentSheetIndex = sheet.Item1.SheetIdx; - var rows = CreateSheetXml(sheet.Item2, sheet.Item1.Path); - rowsWritten.Add(rows); - } + AddFilesToZip(); - GenerateEndXml(); - _archive.Dispose(); + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(_currentSheetIndex - 1))?.Delete(); + GenerateDrawinRelXml(_currentSheetIndex - 1); - return rowsWritten.ToArray(); - } + _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(_currentSheetIndex - 1))?.Delete(); + GenerateDrawingXml(_currentSheetIndex - 1); - public int Insert(bool overwriteSheet = false) + GenerateWorkBookXmls(out StringBuilder workbookXml, out StringBuilder workbookRelsXml, out Dictionary sheetsRelsXml); + foreach (var sheetRelsXml in sheetsRelsXml) { - if (!_configuration.FastMode) - { - throw new InvalidOperationException("Insert requires fast mode to be enabled"); - } + var sheetRelsXmlPath = ExcelFileNames.SheetRels(sheetRelsXml.Key); + _archive.Entries.SingleOrDefault(s => s.FullName == sheetRelsXmlPath)?.Delete(); + CreateZipEntry(sheetRelsXmlPath, null, ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value)); + } - 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"); - } + _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())); - GenerateStylesXml();//GenerateStylesXml必须在校验overwriteSheet之后,避免不必要的样式更改 - int rowsWritten; - if (existSheetDto == null) + InsertContentTypesXml(); + + _archive.Dispose(); + + return rowsWritten; + } + + internal void GenerateDefaultOpenXml() + { + CreateZipEntry(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels); + CreateZipEntry(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString); + GenerateStylesXml(); + } + + private int CreateSheetXml(object values, string sheetPath) + { + var entry = _archive.CreateEntry(sheetPath, CompressionLevel.Fastest); + var rowsWritten = 0; + + using (var zipStream = entry.Open()) + using (var writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize)) + { + if (values == null) { - _currentSheetIndex = (int)sheetRecords.Max(m => m.Id) + 1; - var insertSheetInfo = GetSheetInfos(_defaultSheetName); - var insertSheetDto = insertSheetInfo.ToDto(_currentSheetIndex); - _sheets.Add(insertSheetDto); - rowsWritten = CreateSheetXml(_value, insertSheetDto.Path); + WriteEmptySheet(writer); } else { - _currentSheetIndex = existSheetDto.SheetIdx; - _archive.Entries.Single(s => s.FullName == existSheetDto.Path).Delete(); - rowsWritten = CreateSheetXml(_value, existSheetDto.Path); + rowsWritten = WriteValues(writer, values); } + } + _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); + return rowsWritten; + } - AddFilesToZip(); + private static void WriteEmptySheet(MiniExcelStreamWriter writer) + { + writer.Write(ExcelXml.EmptySheetXml); + } - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.DrawingRels(_currentSheetIndex - 1))?.Delete(); - GenerateDrawinRelXml(_currentSheetIndex - 1); + private static long WriteDimensionPlaceholder(MiniExcelStreamWriter writer) + { + var dimensionPlaceholderPostition = writer.WriteAndFlush(WorksheetXml.StartDimension); + writer.Write(WorksheetXml.DimensionPlaceholder); // end of code will be replaced - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Drawing(_currentSheetIndex - 1))?.Delete(); - GenerateDrawingXml(_currentSheetIndex - 1); + return dimensionPlaceholderPostition; + } - 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)); - } + private static void WriteDimension(MiniExcelStreamWriter writer, int maxRowIndex, int maxColumnIndex, long placeholderPosition) + { + // Flush and save position so that we can get back again. + var position = writer.Flush(); - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.Workbook)?.Delete(); - CreateZipEntry(ExcelFileNames.Workbook, ExcelContentTypes.Workbook, ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString())); + writer.SetPosition(placeholderPosition); + writer.WriteAndFlush($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}"""); - _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.WorkbookRels)?.Delete(); - CreateZipEntry(ExcelFileNames.WorkbookRels, null, ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString())); + writer.SetPosition(position); + } - InsertContentTypesXml(); + private int WriteValues(MiniExcelStreamWriter writer, object values) + { + var writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); - _archive.Dispose(); - - return rowsWritten; + var isKnownCount = writeAdapter.TryGetKnownCount(out var count); + var props = writeAdapter.GetColumns(); + if (props == null) + { + WriteEmptySheet(writer); + return 0; } + + int maxRowIndex; + var maxColumnIndex = props.Count(x => x != null && !x.ExcelIgnore); - internal void GenerateDefaultOpenXml() + writer.Write(WorksheetXml.StartWorksheetWithRelationship); + + long dimensionPlaceholderPostition = 0; + + // We can write the dimensions directly if the row count is known + if (isKnownCount) { - CreateZipEntry(ExcelFileNames.Rels, ExcelContentTypes.Relationships, ExcelXml.DefaultRels); - CreateZipEntry(ExcelFileNames.SharedStrings, ExcelContentTypes.SharedStrings, ExcelXml.DefaultSharedString); - GenerateStylesXml(); + maxRowIndex = _printHeader ? count + 1 : count; + writer.Write(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, maxColumnIndex))); } - - private int CreateSheetXml(object values, string sheetPath) + else if (_configuration.FastMode) { - var entry = _archive.CreateEntry(sheetPath, CompressionLevel.Fastest); - var rowsWritten = 0; - - using (var zipStream = entry.Open()) - using (var writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize)) - { - if (values == null) - { - WriteEmptySheet(writer); - } - else - { - rowsWritten = WriteValues(writer, values); - } - } - _zipDictionary.Add(sheetPath, new ZipPackageInfo(entry, ExcelContentTypes.Worksheet)); - return rowsWritten; + dimensionPlaceholderPostition = WriteDimensionPlaceholder(writer); } - private static void WriteEmptySheet(MiniExcelStreamWriter writer) + //sheet view + writer.Write(GetSheetViews()); + + //cols:width + ExcelWidthCollection widths = null; + long columnWidthsPlaceholderPosition = 0; + if (_configuration.EnableAutoWidth) { - writer.Write(ExcelXml.EmptySheetXml); + columnWidthsPlaceholderPosition = WriteColumnWidthPlaceholders(writer, maxColumnIndex); + widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth); } - - private static long WriteDimensionPlaceholder(MiniExcelStreamWriter writer) + else { - var dimensionPlaceholderPostition = writer.WriteAndFlush(WorksheetXml.StartDimension); - writer.Write(WorksheetXml.DimensionPlaceholder); // end of code will be replaced - - return dimensionPlaceholderPostition; + WriteColumnsWidths(writer, ExcelWidthCollection.FromProps(props)); } - private static void WriteDimension(MiniExcelStreamWriter writer, int maxRowIndex, int maxColumnIndex, long placeholderPosition) + //header + writer.Write(WorksheetXml.StartSheetData); + var currentRowIndex = 0; + if (_printHeader) { - // Flush and save position so that we can get back again. - var position = writer.Flush(); - - writer.SetPosition(placeholderPosition); - writer.WriteAndFlush($@"{GetDimensionRef(maxRowIndex, maxColumnIndex)}"""); - - writer.SetPosition(position); + PrintHeader(writer, props); + currentRowIndex++; } - private int WriteValues(MiniExcelStreamWriter writer, object values) + foreach (var row in writeAdapter.GetRows(props)) { - var writeAdapter = MiniExcelWriteAdapterFactory.GetWriteAdapter(values, _configuration); - - var isKnownCount = writeAdapter.TryGetKnownCount(out var count); - var props = writeAdapter.GetColumns(); - if (props == null) + writer.Write(WorksheetXml.StartRow(++currentRowIndex)); + foreach (var cellValue in row) { - WriteEmptySheet(writer); - return 0; + WriteCell(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths); } - - int maxRowIndex; - var maxColumnIndex = props.Count(x => x != null && !x.ExcelIgnore); + writer.Write(WorksheetXml.EndRow); + } + maxRowIndex = currentRowIndex; - writer.Write(WorksheetXml.StartWorksheetWithRelationship); + writer.Write(WorksheetXml.EndSheetData); - long dimensionPlaceholderPostition = 0; + if (_configuration.AutoFilter) + writer.Write(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex))); - // We can write the dimensions directly if the row count is known - if (isKnownCount) - { - maxRowIndex = _printHeader ? count + 1 : count; - writer.Write(WorksheetXml.Dimension(GetDimensionRef(maxRowIndex, maxColumnIndex))); - } - else if (_configuration.FastMode) - { - dimensionPlaceholderPostition = WriteDimensionPlaceholder(writer); - } + writer.Write(WorksheetXml.Drawing(_currentSheetIndex)); + writer.Write(WorksheetXml.EndWorksheet); - //sheet view - writer.Write(GetSheetViews()); + if (_configuration.FastMode && dimensionPlaceholderPostition != 0) + { + WriteDimension(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition); + } + if (_configuration.EnableAutoWidth) + { + OverwriteColumnWidthPlaceholders(writer, columnWidthsPlaceholderPosition, widths?.Columns); + } - //cols:width - ExcelWidthCollection widths = null; - long columnWidthsPlaceholderPosition = 0; - if (_configuration.EnableAutoWidth) - { - columnWidthsPlaceholderPosition = WriteColumnWidthPlaceholders(writer, maxColumnIndex); - widths = ExcelWidthCollection.FromProps(props, _configuration.MinWidth, _configuration.MaxWidth); - } - else - { - WriteColumnsWidths(writer, ExcelWidthCollection.FromProps(props)); - } + if (_printHeader) + maxRowIndex--; - //header - writer.Write(WorksheetXml.StartSheetData); - var currentRowIndex = 0; - if (_printHeader) - { - PrintHeader(writer, props); - currentRowIndex++; - } + return maxRowIndex; + } - foreach (var row in writeAdapter.GetRows(props)) - { - writer.Write(WorksheetXml.StartRow(++currentRowIndex)); - foreach (var cellValue in row) - { - WriteCell(writer, currentRowIndex, cellValue.CellIndex, cellValue.Value, cellValue.Prop, widths); - } - writer.Write(WorksheetXml.EndRow); - } - maxRowIndex = currentRowIndex; + private static long WriteColumnWidthPlaceholders(MiniExcelStreamWriter writer, int count) + { + var placeholderPosition = writer.Flush(); + writer.WriteWhitespace(WorksheetXml.GetColumnPlaceholderLength(count)); + return placeholderPosition; + } - writer.Write(WorksheetXml.EndSheetData); + private static void OverwriteColumnWidthPlaceholders(MiniExcelStreamWriter writer, long placeholderPosition, IEnumerable columnWidths) + { + var position = writer.Flush(); - if (_configuration.AutoFilter) - writer.Write(WorksheetXml.Autofilter(GetDimensionRef(maxRowIndex, maxColumnIndex))); + writer.SetPosition(placeholderPosition); + WriteColumnsWidths(writer, columnWidths); - writer.Write(WorksheetXml.Drawing(_currentSheetIndex)); - writer.Write(WorksheetXml.EndWorksheet); + writer.Flush(); + writer.SetPosition(position); + } - if (_configuration.FastMode && dimensionPlaceholderPostition != 0) - { - WriteDimension(writer, maxRowIndex, maxColumnIndex, dimensionPlaceholderPostition); - } - if (_configuration.EnableAutoWidth) + private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable columnWidths) + { + var hasWrittenStart = false; + foreach (var column in columnWidths) + { + if (!hasWrittenStart) { - OverwriteColumnWidthPlaceholders(writer, columnWidthsPlaceholderPosition, widths?.Columns); + writer.Write(WorksheetXml.StartCols); + hasWrittenStart = true; } - - if (_printHeader) - maxRowIndex--; - - return maxRowIndex; + writer.Write(WorksheetXml.Column(column.Index, column.Width, column.Hidden)); } - private static long WriteColumnWidthPlaceholders(MiniExcelStreamWriter writer, int count) + if (hasWrittenStart) { - var placeholderPosition = writer.Flush(); - writer.WriteWhitespace(WorksheetXml.GetColumnPlaceholderLength(count)); - return placeholderPosition; + writer.Write(WorksheetXml.EndCols); } + } - private static void OverwriteColumnWidthPlaceholders(MiniExcelStreamWriter writer, long placeholderPosition, IEnumerable columnWidths) - { - var position = writer.Flush(); - - writer.SetPosition(placeholderPosition); - WriteColumnsWidths(writer, columnWidths); - - writer.Flush(); - writer.SetPosition(position); - } + private void PrintHeader(MiniExcelStreamWriter writer, List props) + { + const int yIndex = 1; + writer.Write(WorksheetXml.StartRow(yIndex)); - private static void WriteColumnsWidths(MiniExcelStreamWriter writer, IEnumerable columnWidths) + var xIndex = 1; + foreach (var p in props) { - var hasWrittenStart = false; - foreach (var column in columnWidths) - { - if (!hasWrittenStart) - { - writer.Write(WorksheetXml.StartCols); - hasWrittenStart = true; - } - writer.Write(WorksheetXml.Column(column.Index, column.Width, column.Hidden)); - } - - if (hasWrittenStart) + //reason : https://github.com/mini-software/MiniExcel/issues/142 + if (p != null) { - writer.Write(WorksheetXml.EndCols); + if (p.ExcelIgnore) + continue; + + var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex); + WriteCell(writer, r, columnName: p.ExcelColumnName); } + xIndex++; } - private void PrintHeader(MiniExcelStreamWriter writer, List props) - { - const int yIndex = 1; - writer.Write(WorksheetXml.StartRow(yIndex)); + writer.Write(WorksheetXml.EndRow); + } - var xIndex = 1; - foreach (var p in props) + private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo columnInfo, ExcelWidthCollection widthCollection) + { + if (columnInfo?.CustomFormatter != null) + { + try { - //reason : https://github.com/mini-software/MiniExcel/issues/142 - if (p != null) - { - if (p.ExcelIgnore) - continue; - - var r = ExcelOpenXmlUtils.ConvertXyToCell(xIndex, yIndex); - WriteCell(writer, r, columnName: p.ExcelColumnName); - } - xIndex++; + value = columnInfo.CustomFormatter(value); } - - writer.Write(WorksheetXml.EndRow); - } - - private void WriteCell(MiniExcelStreamWriter writer, int rowIndex, int cellIndex, object value, ExcelColumnInfo columnInfo, ExcelWidthCollection widthCollection) - { - if (columnInfo?.CustomFormatter != null) + catch { - try - { - value = columnInfo.CustomFormatter(value); - } - catch - { - //ignored - } + //ignored } + } - var columnReference = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, rowIndex); - var valueIsNull = value is null || - value is DBNull || - (_configuration.WriteEmptyStringAsNull && value is string vs && vs == string.Empty); + var columnReference = ExcelOpenXmlUtils.ConvertXyToCell(cellIndex, rowIndex); + var valueIsNull = value is null || + value is DBNull || + (_configuration.WriteEmptyStringAsNull && value is string vs && vs == string.Empty); - if (_configuration.EnableWriteNullValueCell && valueIsNull) - { - writer.Write(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); - return; - } + if (_configuration.EnableWriteNullValueCell && valueIsNull) + { + writer.Write(WorksheetXml.EmptyCell(columnReference, GetCellXfId("2"))); + return; + } - var tuple = GetCellValue(rowIndex, cellIndex, value, columnInfo, valueIsNull); + var tuple = GetCellValue(rowIndex, cellIndex, value, columnInfo, valueIsNull); - var styleIndex = tuple.Item1; // https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.cell?view=openxml-3.0.1 - var dataType = tuple.Item2; // https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.cellvalues?view=openxml-3.0.1 - var cellValue = tuple.Item3; + var styleIndex = tuple.Item1; // https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.cell?view=openxml-3.0.1 + var dataType = tuple.Item2; // https://learn.microsoft.com/en-us/dotnet/api/documentformat.openxml.spreadsheet.cellvalues?view=openxml-3.0.1 + var cellValue = tuple.Item3; - var columnType = columnInfo?.ExcelColumnType ?? ColumnType.Value; + var columnType = columnInfo?.ExcelColumnType ?? ColumnType.Value; - /*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, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); - widthCollection?.AdjustWidth(cellIndex, cellValue); - } + /*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, GetCellXfId(styleIndex), cellValue, preserveSpace: preserveSpace, columnType: columnType)); + widthCollection?.AdjustWidth(cellIndex, cellValue); + } - private void WriteCell(MiniExcelStreamWriter writer, string cellReference, string columnName) - => writer.Write(WorksheetXml.Cell(cellReference, "str", GetCellXfId("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() - { - AddFilesToZip(); - GenerateDrawinRelXml(); - GenerateDrawingXml(); - GenerateWorkbookXml(); - GenerateContentTypesXml(); - } + private void GenerateEndXml() + { + AddFilesToZip(); + GenerateDrawinRelXml(); + GenerateDrawingXml(); + GenerateWorkbookXml(); + GenerateContentTypesXml(); + } - private void AddFilesToZip() + private void AddFilesToZip() + { + foreach (var item in _files) { - foreach (var item in _files) - { - CreateZipEntry(item.Path, item.Byte); - } + CreateZipEntry(item.Path, item.Byte); } + } - private void GenerateStylesXml() + private void GenerateStylesXml() + { + using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) { - using (var context = new SheetStyleBuildContext(_zipDictionary, _archive, _utf8WithBom, _configuration.DynamicColumns)) - { - ISheetStyleBuilder builder = null; - switch (_configuration.TableStyles) - { - case TableStyles.None: - builder = new MinimalSheetStyleBuilder(context); - break; - case TableStyles.Default: - builder = new DefaultSheetStyleBuilder(context, _configuration.StyleOptions); - break; - } - var result = builder?.Build(); - _cellXfIdMap = result?.CellXfIdMap; - } + ISheetStyleBuilder builder = null; + switch (_configuration.TableStyles) + { + case TableStyles.None: + builder = new MinimalSheetStyleBuilder(context); + break; + case TableStyles.Default: + builder = new DefaultSheetStyleBuilder(context, _configuration.StyleOptions); + break; + } + var result = builder?.Build(); + _cellXfIdMap = result?.CellXfIdMap; } + } - private void GenerateDrawinRelXml() + private void GenerateDrawinRelXml() + { + for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) - { - GenerateDrawinRelXml(sheetIndex); - } + GenerateDrawinRelXml(sheetIndex); } + } - private void GenerateDrawinRelXml(int sheetIndex) - { - var drawing = GetDrawingRelationshipXml(sheetIndex); - CreateZipEntry( - ExcelFileNames.DrawingRels(sheetIndex), - null, - ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); - } + private void GenerateDrawinRelXml(int sheetIndex) + { + var drawing = GetDrawingRelationshipXml(sheetIndex); + CreateZipEntry( + ExcelFileNames.DrawingRels(sheetIndex), + null, + ExcelXml.DefaultDrawingXmlRels.Replace("{{format}}", drawing)); + } - private void GenerateDrawingXml() + private void GenerateDrawingXml() + { + for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) { - for (int sheetIndex = 0; sheetIndex < _sheets.Count; sheetIndex++) - { - GenerateDrawingXml(sheetIndex); - } + GenerateDrawingXml(sheetIndex); } + } + + private void GenerateDrawingXml(int sheetIndex) + { + var drawing = GetDrawingXml(sheetIndex); + CreateZipEntry( + ExcelFileNames.Drawing(sheetIndex), + ExcelContentTypes.Drawing, + ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + } - private void GenerateDrawingXml(int sheetIndex) + private void GenerateWorkbookXml() + { + GenerateWorkBookXmls( + out StringBuilder workbookXml, + out StringBuilder workbookRelsXml, + out Dictionary sheetsRelsXml); + + foreach (var sheetRelsXml in sheetsRelsXml) { - var drawing = GetDrawingXml(sheetIndex); CreateZipEntry( - ExcelFileNames.Drawing(sheetIndex), - ExcelContentTypes.Drawing, - ExcelXml.DefaultDrawing.Replace("{{format}}", drawing)); + ExcelFileNames.SheetRels(sheetRelsXml.Key), + null, + ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value)); } - private void GenerateWorkbookXml() - { - GenerateWorkBookXmls( - out StringBuilder workbookXml, - out StringBuilder workbookRelsXml, - out Dictionary sheetsRelsXml); + CreateZipEntry( + ExcelFileNames.Workbook, + ExcelContentTypes.Workbook, + ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString())); - foreach (var sheetRelsXml in sheetsRelsXml) - { - CreateZipEntry( - ExcelFileNames.SheetRels(sheetRelsXml.Key), - null, - ExcelXml.DefaultSheetRelXml.Replace("{{format}}", sheetRelsXml.Value)); - } + CreateZipEntry( + ExcelFileNames.WorkbookRels, + null, + ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString())); + } - CreateZipEntry( - ExcelFileNames.Workbook, - ExcelContentTypes.Workbook, - ExcelXml.DefaultWorkbookXml.Replace("{{sheets}}", workbookXml.ToString())); + private void GenerateContentTypesXml() + { + var contentTypes = GetContentTypesXml(); - CreateZipEntry( - ExcelFileNames.WorkbookRels, - null, - ExcelXml.DefaultWorkbookXmlRels.Replace("{{sheets}}", workbookRelsXml.ToString())); - } + CreateZipEntry(ExcelFileNames.ContentTypes, null, contentTypes); + } - private void GenerateContentTypesXml() + private void InsertContentTypesXml() + { + var contentTypesZipEntry = _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.ContentTypes); + if (contentTypesZipEntry == null) { - var contentTypes = GetContentTypesXml(); - - CreateZipEntry(ExcelFileNames.ContentTypes, null, contentTypes); + GenerateContentTypesXml(); + return; } - - private void InsertContentTypesXml() + using (var stream = contentTypesZipEntry.Open()) { - var contentTypesZipEntry = _archive.Entries.SingleOrDefault(s => s.FullName == ExcelFileNames.ContentTypes); - if (contentTypesZipEntry == null) + var doc = XDocument.Load(stream); + var ns = doc.Root?.GetDefaultNamespace(); + var typesElement = doc.Descendants(ns + "Types").Single(); + var partNames = new HashSet(StringComparer.InvariantCultureIgnoreCase); + foreach (var partName in typesElement.Elements(ns + "Override").Select(s => s.Attribute("PartName")?.Value)) { - GenerateContentTypesXml(); - return; + partNames.Add(partName); } - using (var stream = contentTypesZipEntry.Open()) + foreach (var p in _zipDictionary) { - var doc = XDocument.Load(stream); - var ns = doc.Root?.GetDefaultNamespace(); - var typesElement = doc.Descendants(ns + "Types").Single(); - var partNames = new HashSet(StringComparer.InvariantCultureIgnoreCase); - foreach (var partName in typesElement.Elements(ns + "Override").Select(s => s.Attribute("PartName")?.Value)) - { - partNames.Add(partName); - } - foreach (var p in _zipDictionary) + var partName = $"/{p.Key}"; + if (!partNames.Contains(partName)) { - var partName = $"/{p.Key}"; - if (!partNames.Contains(partName)) - { - var newElement = new XElement(ns + "Override", new XAttribute("ContentType", p.Value.ContentType), new XAttribute("PartName", partName)); - typesElement.Add(newElement); - } + var newElement = new XElement(ns + "Override", new XAttribute("ContentType", p.Value.ContentType), new XAttribute("PartName", partName)); + typesElement.Add(newElement); } - stream.Position = 0; - doc.Save(stream); } + stream.Position = 0; + doc.Save(stream); } + } - private void CreateZipEntry(string path, string contentType, string content) + private void CreateZipEntry(string path, string contentType, string content) + { + ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest); + using (var zipStream = entry.Open()) + using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize)) { - ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest); - using (var zipStream = entry.Open()) - using (MiniExcelStreamWriter writer = new MiniExcelStreamWriter(zipStream, _utf8WithBom, _configuration.BufferSize)) - { - writer.Write(content); - } + writer.Write(content); + } - if (!string.IsNullOrEmpty(contentType)) - { - _zipDictionary.Add(path, new ZipPackageInfo(entry, contentType)); - } + if (!string.IsNullOrEmpty(contentType)) + { + _zipDictionary.Add(path, new ZipPackageInfo(entry, contentType)); } + } - private void CreateZipEntry(string path, byte[] content) + private void CreateZipEntry(string path, byte[] content) + { + ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest); + using (var zipStream = entry.Open()) { - ZipArchiveEntry entry = _archive.CreateEntry(path, CompressionLevel.Fastest); - using (var zipStream = entry.Open()) - { - zipStream.Write(content, 0, content.Length); - } + zipStream.Write(content, 0, content.Length); } } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs index 20c5f123..fc00794d 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlStyles.cs @@ -1,207 +1,203 @@ using MiniExcelLibs.Utils; using MiniExcelLibs.Zip; -using System; -using System.Collections.Generic; -using System.Diagnostics; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal class ExcelOpenXmlStyles { - internal class ExcelOpenXmlStyles - { - private static readonly string[] _ns = { Config.SpreadsheetmlXmlns, Config.SpreadsheetmlXmlStrictns }; - private readonly Dictionary _cellXfs = new Dictionary(); - private readonly Dictionary _cellStyleXfs = new Dictionary(); - private readonly Dictionary _customFormats = new Dictionary(); + private static readonly string[] _ns = { Config.SpreadsheetmlXmlns, Config.SpreadsheetmlXmlStrictns }; + private readonly Dictionary _cellXfs = new Dictionary(); + private readonly Dictionary _cellStyleXfs = new Dictionary(); + private readonly Dictionary _customFormats = new Dictionary(); - public ExcelOpenXmlStyles(ExcelOpenXmlZip zip) + public ExcelOpenXmlStyles(ExcelOpenXmlZip zip) + { + using (var reader = zip.GetXmlReader(@"xl/styles.xml")) { - using (var reader = zip.GetXmlReader(@"xl/styles.xml")) - { - if (!XmlReaderHelper.IsStartElement(reader, "styleSheet", _ns)) - return; - if (!XmlReaderHelper.ReadFirstContent(reader)) - return; + if (!XmlReaderHelper.IsStartElement(reader, "styleSheet", _ns)) + return; + if (!XmlReaderHelper.ReadFirstContent(reader)) + return; - while (!reader.EOF) + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "cellXfs", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "cellXfs", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - var index = 0; - while (!reader.EOF) + var index = 0; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "xf", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "xf", _ns)) - { - int.TryParse(reader.GetAttribute("xfId"), out var xfId); - int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); - _cellXfs.Add(index, new StyleRecord() { XfId = xfId, NumFmtId = numFmtId }); - reader.Skip(); - index++; - } - else if (!XmlReaderHelper.SkipContent(reader)) - break; + int.TryParse(reader.GetAttribute("xfId"), out var xfId); + int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); + _cellXfs.Add(index, new StyleRecord() { XfId = xfId, NumFmtId = numFmtId }); + reader.Skip(); + index++; } + else if (!XmlReaderHelper.SkipContent(reader)) + break; } - else if (XmlReaderHelper.IsStartElement(reader, "cellStyleXfs", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; + } + else if (XmlReaderHelper.IsStartElement(reader, "cellStyleXfs", _ns)) + { + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - var index = 0; - while (!reader.EOF) + var index = 0; + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "xf", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "xf", _ns)) - { - int.TryParse(reader.GetAttribute("xfId"), out var xfId); - int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); + int.TryParse(reader.GetAttribute("xfId"), out var xfId); + int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); - _cellStyleXfs.Add(index, new StyleRecord() { XfId = xfId, NumFmtId = numFmtId }); - reader.Skip(); - index++; - } - else if (!XmlReaderHelper.SkipContent(reader)) - break; + _cellStyleXfs.Add(index, new StyleRecord() { XfId = xfId, NumFmtId = numFmtId }); + reader.Skip(); + index++; } + else if (!XmlReaderHelper.SkipContent(reader)) + break; } - else if (XmlReaderHelper.IsStartElement(reader, "numFmts", _ns)) - { - if (!XmlReaderHelper.ReadFirstContent(reader)) - continue; + } + else if (XmlReaderHelper.IsStartElement(reader, "numFmts", _ns)) + { + if (!XmlReaderHelper.ReadFirstContent(reader)) + continue; - while (!reader.EOF) + while (!reader.EOF) + { + if (XmlReaderHelper.IsStartElement(reader, "numFmt", _ns)) { - if (XmlReaderHelper.IsStartElement(reader, "numFmt", _ns)) - { - int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); - var formatCode = reader.GetAttribute("formatCode"); + int.TryParse(reader.GetAttribute("numFmtId"), out var numFmtId); + var formatCode = reader.GetAttribute("formatCode"); - //TODO: determine the type according to the format - var type = typeof(string); - if (DateTimeHelper.IsDateTimeFormat(formatCode)) - { - type = typeof(DateTime?); - } - - if (!_customFormats.ContainsKey(numFmtId)) - _customFormats.Add(numFmtId, new NumberFormatString(formatCode, type)); - reader.Skip(); - } - else if (!XmlReaderHelper.SkipContent(reader)) + //TODO: determine the type according to the format + var type = typeof(string); + if (DateTimeHelper.IsDateTimeFormat(formatCode)) { - break; + type = typeof(DateTime?); } + + if (!_customFormats.ContainsKey(numFmtId)) + _customFormats.Add(numFmtId, new NumberFormatString(formatCode, type)); + reader.Skip(); + } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; } } - else if (!XmlReaderHelper.SkipContent(reader)) - { - break; - } + } + else if (!XmlReaderHelper.SkipContent(reader)) + { + break; } } } + } - public NumberFormatString GetStyleFormat(int index) - { - if (!_cellXfs.TryGetValue(index, out var styleRecord)) - return null; + public NumberFormatString GetStyleFormat(int index) + { + if (!_cellXfs.TryGetValue(index, out var styleRecord)) + return null; - if (Formats.TryGetValue(styleRecord.NumFmtId, out var numberFormat)) - return numberFormat; + if (Formats.TryGetValue(styleRecord.NumFmtId, out var numberFormat)) + return numberFormat; - if (_customFormats.TryGetValue(styleRecord.NumFmtId, out var customNumberFormat)) - return customNumberFormat; + if (_customFormats.TryGetValue(styleRecord.NumFmtId, out var customNumberFormat)) + return customNumberFormat; - return null; - } + return null; + } - public object ConvertValueByStyleFormat(int index, object value) - { - var sf = GetStyleFormat(index); - if (sf?.Type == null) - return value; + public object ConvertValueByStyleFormat(int index, object value) + { + var sf = GetStyleFormat(index); + if (sf?.Type == null) + return value; - if (sf.Type == typeof(DateTime?)) - { - if (double.TryParse(value?.ToString(), out var s)) - { - return DateTimeHelper.IsValidOADateTime(s) ? DateTime.FromOADate(s) : value; - } - } - else if (sf.Type == typeof(TimeSpan?)) + if (sf.Type == typeof(DateTime?)) + { + if (double.TryParse(value?.ToString(), out var s)) { - if (double.TryParse(value?.ToString(), out var number)) - return TimeSpan.FromDays(number); + return DateTimeHelper.IsValidOADateTime(s) ? DateTime.FromOADate(s) : value; } - - return value; } - - private static Dictionary Formats { get; } = new Dictionary + else if (sf.Type == typeof(TimeSpan?)) { - { 0, new NumberFormatString("General",typeof(string)) }, - { 1, new NumberFormatString("0",typeof(decimal?)) }, - { 2, new NumberFormatString("0.00",typeof(decimal?)) }, - { 3, new NumberFormatString("#,##0",typeof(decimal?)) }, - { 4, new NumberFormatString("#,##0.00",typeof(decimal?)) }, - { 5, new NumberFormatString("\"$\"#,##0_);(\"$\"#,##0)",typeof(decimal?)) }, - { 6, new NumberFormatString("\"$\"#,##0_);[Red](\"$\"#,##0)",typeof(decimal?)) }, - { 7, new NumberFormatString("\"$\"#,##0.00_);(\"$\"#,##0.00)",typeof(decimal?)) }, - { 8, new NumberFormatString("\"$\"#,##0.00_);[Red](\"$\"#,##0.00)",typeof(string)) }, - { 9, new NumberFormatString("0%",typeof(decimal?)) }, - { 10, new NumberFormatString("0.00%",typeof(string)) }, - { 11, new NumberFormatString("0.00E+00",typeof(string)) }, - { 12, new NumberFormatString("# ?/?",typeof(string)) }, - { 13, new NumberFormatString("# ??/??",typeof(string)) }, - { 14, new NumberFormatString("d/m/yyyy",typeof(DateTime?)) }, - { 15, new NumberFormatString("d-mmm-yy",typeof(DateTime?)) }, - { 16, new NumberFormatString("d-mmm",typeof(DateTime?)) }, - { 17, new NumberFormatString("mmm-yy",typeof(TimeSpan)) }, - { 18, new NumberFormatString("h:mm AM/PM",typeof(TimeSpan?)) }, - { 19, new NumberFormatString("h:mm:ss AM/PM",typeof(TimeSpan?)) }, - { 20, new NumberFormatString("h:mm",typeof(TimeSpan?)) }, - { 21, new NumberFormatString("h:mm:ss",typeof(TimeSpan?)) }, - { 22, new NumberFormatString("m/d/yy h:mm",typeof(DateTime?)) }, - // 23..36 international/unused - { 37, new NumberFormatString("#,##0_);(#,##0)",typeof(string)) }, - { 38, new NumberFormatString("#,##0_);[Red](#,##0)",typeof(string)) }, - { 39, new NumberFormatString("#,##0.00_);(#,##0.00)",typeof(string)) }, - { 40, new NumberFormatString("#,##0.00_);[Red](#,##0.00)",typeof(string)) }, - { 41, new NumberFormatString("_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)",typeof(string)) }, - { 42, new NumberFormatString("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",typeof(string)) }, - { 43, new NumberFormatString("_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)",typeof(string)) }, - { 44, new NumberFormatString("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",typeof(string)) }, - { 45, new NumberFormatString("mm:ss",typeof(TimeSpan?)) }, - { 46, new NumberFormatString("[h]:mm:ss",typeof(TimeSpan?)) }, - { 47, new NumberFormatString("mm:ss.0",typeof(TimeSpan?)) }, - { 48, new NumberFormatString("##0.0E+0",typeof(string)) }, - { 49, new NumberFormatString("@",typeof(string)) }, - - // issue 222 - { 58, new NumberFormatString("m/d",typeof(DateTime?)) }, - - // custom format start with 176 - }; + if (double.TryParse(value?.ToString(), out var number)) + return TimeSpan.FromDays(number); + } + + return value; } - internal class NumberFormatString + private static Dictionary Formats { get; } = new Dictionary { - public string FormatCode { get; } - public Type Type { get; set; } - public bool NeedConvertToString { get; } - - public NumberFormatString(string formatCode, Type type, bool needConvertToString = false) - { - FormatCode = formatCode; - Type = type; - NeedConvertToString = needConvertToString; - } - } + { 0, new NumberFormatString("General",typeof(string)) }, + { 1, new NumberFormatString("0",typeof(decimal?)) }, + { 2, new NumberFormatString("0.00",typeof(decimal?)) }, + { 3, new NumberFormatString("#,##0",typeof(decimal?)) }, + { 4, new NumberFormatString("#,##0.00",typeof(decimal?)) }, + { 5, new NumberFormatString("\"$\"#,##0_);(\"$\"#,##0)",typeof(decimal?)) }, + { 6, new NumberFormatString("\"$\"#,##0_);[Red](\"$\"#,##0)",typeof(decimal?)) }, + { 7, new NumberFormatString("\"$\"#,##0.00_);(\"$\"#,##0.00)",typeof(decimal?)) }, + { 8, new NumberFormatString("\"$\"#,##0.00_);[Red](\"$\"#,##0.00)",typeof(string)) }, + { 9, new NumberFormatString("0%",typeof(decimal?)) }, + { 10, new NumberFormatString("0.00%",typeof(string)) }, + { 11, new NumberFormatString("0.00E+00",typeof(string)) }, + { 12, new NumberFormatString("# ?/?",typeof(string)) }, + { 13, new NumberFormatString("# ??/??",typeof(string)) }, + { 14, new NumberFormatString("d/m/yyyy",typeof(DateTime?)) }, + { 15, new NumberFormatString("d-mmm-yy",typeof(DateTime?)) }, + { 16, new NumberFormatString("d-mmm",typeof(DateTime?)) }, + { 17, new NumberFormatString("mmm-yy",typeof(TimeSpan)) }, + { 18, new NumberFormatString("h:mm AM/PM",typeof(TimeSpan?)) }, + { 19, new NumberFormatString("h:mm:ss AM/PM",typeof(TimeSpan?)) }, + { 20, new NumberFormatString("h:mm",typeof(TimeSpan?)) }, + { 21, new NumberFormatString("h:mm:ss",typeof(TimeSpan?)) }, + { 22, new NumberFormatString("m/d/yy h:mm",typeof(DateTime?)) }, + // 23..36 international/unused + { 37, new NumberFormatString("#,##0_);(#,##0)",typeof(string)) }, + { 38, new NumberFormatString("#,##0_);[Red](#,##0)",typeof(string)) }, + { 39, new NumberFormatString("#,##0.00_);(#,##0.00)",typeof(string)) }, + { 40, new NumberFormatString("#,##0.00_);[Red](#,##0.00)",typeof(string)) }, + { 41, new NumberFormatString("_(\"$\"* #,##0_);_(\"$\"* (#,##0);_(\"$\"* \"-\"_);_(@_)",typeof(string)) }, + { 42, new NumberFormatString("_(* #,##0_);_(* (#,##0);_(* \"-\"_);_(@_)",typeof(string)) }, + { 43, new NumberFormatString("_(\"$\"* #,##0.00_);_(\"$\"* (#,##0.00);_(\"$\"* \"-\"??_);_(@_)",typeof(string)) }, + { 44, new NumberFormatString("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)",typeof(string)) }, + { 45, new NumberFormatString("mm:ss",typeof(TimeSpan?)) }, + { 46, new NumberFormatString("[h]:mm:ss",typeof(TimeSpan?)) }, + { 47, new NumberFormatString("mm:ss.0",typeof(TimeSpan?)) }, + { 48, new NumberFormatString("##0.0E+0",typeof(string)) }, + { 49, new NumberFormatString("@",typeof(string)) }, + + // issue 222 + { 58, new NumberFormatString("m/d",typeof(DateTime?)) }, + + // custom format start with 176 + }; +} + +internal class NumberFormatString +{ + public string FormatCode { get; } + public Type Type { get; set; } + public bool NeedConvertToString { get; } - internal class StyleRecord + public NumberFormatString(string formatCode, Type type, bool needConvertToString = false) { - public int XfId { get; set; } - public int NumFmtId { get; set; } + FormatCode = formatCode; + Type = type; + NeedConvertToString = needConvertToString; } +} + +internal class StyleRecord +{ + public int XfId { get; set; } + public int NumFmtId { get; set; } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs b/src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs index f58ac5fb..da532933 100644 --- a/src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs +++ b/src/MiniExcel/OpenXml/ExcelOpenXmlUtils.cs @@ -1,113 +1,110 @@ -using System; -using System.Runtime.CompilerServices; -using MiniExcelLibs.Utils; +using MiniExcelLibs.Utils; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal static class ExcelOpenXmlUtils { - internal static class ExcelOpenXmlUtils - { - public static string MinifyXml(string xml) => xml - .Replace("\r", "") - .Replace("\n", "") - .Replace("\t", "") - .Trim(); + public static string MinifyXml(string xml) => xml + .Replace("\r", "") + .Replace("\n", "") + .Replace("\t", "") + .Trim(); - /// - /// Encode to XML (special characteres: ' " > < &) - /// - public static string EncodeXML(string value) => value == null ? string.Empty - : XmlEncoder.EncodeString(value) - .Replace("&", "&") - .Replace("<", "<") - .Replace(">", ">") - .Replace("\"", """) - .Replace("'", "'") - .ToString(); + /// + /// Encode to XML (special characteres: ' " > < &) + /// + public static string EncodeXML(string value) => value == null ? string.Empty + : XmlEncoder.EncodeString(value) + .Replace("&", "&") + .Replace("<", "<") + .Replace(">", ">") + .Replace("\"", """) + .Replace("'", "'") + .ToString(); - /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) - public static string ConvertXyToCell(int x, int y) - { - int dividend = x; - string columnName = string.Empty; + /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) + public static string ConvertXyToCell(int x, int y) + { + int dividend = x; + string columnName = string.Empty; - while (dividend > 0) - { - var modulo = (dividend - 1) % 26; - columnName = Convert.ToChar(65 + modulo) + columnName; - dividend = (dividend - modulo) / 26; - } - return $"{columnName}{y}"; + while (dividend > 0) + { + var modulo = (dividend - 1) % 26; + columnName = Convert.ToChar(65 + modulo) + columnName; + dividend = (dividend - modulo) / 26; } + return $"{columnName}{y}"; + } - /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) - public static string ConvertXyToCell(Tuple xy) => ConvertXyToCell(xy.Item1, xy.Item2); + /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) + public static string ConvertXyToCell(Tuple xy) => ConvertXyToCell(xy.Item1, xy.Item2); - /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) - public static Tuple ConvertCellToXY(string cell) => Tuple.Create(GetCellColumnIndex(cell), GetCellRowNumber(cell)); + /// X=CellLetter,Y=CellNumber,ex:A1=(1,1),B2=(2,2) + public static Tuple ConvertCellToXY(string cell) => Tuple.Create(GetCellColumnIndex(cell), GetCellRowNumber(cell)); - public static int GetColumnNumber(string name) + public static int GetColumnNumber(string name) + { + int number = -1; + int pow = 1; + for (int i = name.Length - 1; i >= 0; i--) { - int number = -1; - int pow = 1; - for (int i = name.Length - 1; i >= 0; i--) - { - number += (name[i] - 'A' + 1) * pow; - pow *= 26; - } - - return number; + number += (name[i] - 'A' + 1) * pow; + pow *= 26; } - public static int GetCellColumnIndex(string cell) - { - const string keys = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const int mode = 26; + return number; + } - var x = 0; - var cellLetter = GetCellColumnLetter(cell); - //AA=27,ZZ=702 - foreach (var t in cellLetter) - x = x * mode + keys.IndexOf(t); + public static int GetCellColumnIndex(string cell) + { + const string keys = " ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + const int mode = 26; - return x; - } + var x = 0; + var cellLetter = GetCellColumnLetter(cell); + //AA=27,ZZ=702 + foreach (var t in cellLetter) + x = x * mode + keys.IndexOf(t); + + return x; + } - public static int GetCellRowNumber(string cell) + public static int GetCellRowNumber(string cell) + { + if (string.IsNullOrEmpty(cell)) + throw new Exception("cell is null or empty"); + var cellNumber = string.Empty; + foreach (var t in cell) { - if (string.IsNullOrEmpty(cell)) - throw new Exception("cell is null or empty"); - var cellNumber = string.Empty; - foreach (var t in cell) - { - if (char.IsDigit(t)) - cellNumber += t; - } - return int.Parse(cellNumber); + if (char.IsDigit(t)) + cellNumber += t; } + return int.Parse(cellNumber); + } - public static string GetCellColumnLetter(string cell) + public static string GetCellColumnLetter(string cell) + { + string GetCellLetter = string.Empty; + foreach (var t in cell) { - string GetCellLetter = string.Empty; - foreach (var t in cell) - { - if (char.IsLetter(t)) - GetCellLetter += t; - } - return GetCellLetter; + if (char.IsLetter(t)) + GetCellLetter += t; } + return GetCellLetter; + } - public static string ConvertColumnName(int x) - { - int dividend = x; - string columnName = string.Empty; + public static string ConvertColumnName(int x) + { + int dividend = x; + string columnName = string.Empty; - while (dividend > 0) - { - var modulo = (dividend - 1) % 26; - columnName = Convert.ToChar(65 + modulo) + columnName; - dividend = (dividend - modulo) / 26; - } - return columnName; + while (dividend > 0) + { + var modulo = (dividend - 1) % 26; + columnName = Convert.ToChar(65 + modulo) + columnName; + dividend = (dividend - modulo) / 26; } + return columnName; } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/ExcelWidthCollection.cs b/src/MiniExcel/OpenXml/ExcelWidthCollection.cs index ee74e584..609adae2 100644 --- a/src/MiniExcel/OpenXml/ExcelWidthCollection.cs +++ b/src/MiniExcel/OpenXml/ExcelWidthCollection.cs @@ -1,81 +1,76 @@ using MiniExcelLibs.Utils; -using System; using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +public sealed class ExcelColumnWidth { - public sealed class ExcelColumnWidth - { - // Aptos is the default font for Office 2023 and onwards, over which the width of cells are calculated at the size of 11pt. - // Priorly it was Calibri, which had very similar parameters, so no visual differences should be noticed. - private const double DefaultCellPadding = 5; - private const double Aptos11MaxDigitWidth = 7; - public const double Aptos11Padding = DefaultCellPadding / Aptos11MaxDigitWidth; + // Aptos is the default font for Office 2023 and onwards, over which the width of cells are calculated at the size of 11pt. + // Priorly it was Calibri, which had very similar parameters, so no visual differences should be noticed. + private const double DefaultCellPadding = 5; + private const double Aptos11MaxDigitWidth = 7; + public const double Aptos11Padding = DefaultCellPadding / Aptos11MaxDigitWidth; - public int Index { get; set; } - public double Width { get; set; } - public bool Hidden { get; set; } + public int Index { get; set; } + public double Width { get; set; } + public bool Hidden { get; set; } - public static double GetWidthFromTextLength(double characters) - => Math.Round(characters + Aptos11Padding, 8); - } + public static double GetWidthFromTextLength(double characters) + => Math.Round(characters + Aptos11Padding, 8); +} - public sealed class ExcelWidthCollection : IReadOnlyCollection - { - private readonly Dictionary _columnWidths; - private readonly double _maxWidth; +public sealed class ExcelWidthCollection : IReadOnlyCollection +{ + private readonly Dictionary _columnWidths; + private readonly double _maxWidth; - public IReadOnlyCollection Columns + public IReadOnlyCollection Columns #if NET45 => _columnWidths.Values.ToList(); #else - => _columnWidths.Values; + => _columnWidths.Values; #endif - private ExcelWidthCollection(ICollection columnWidths, double maxWidth) - { - _maxWidth = ExcelColumnWidth.GetWidthFromTextLength(maxWidth); - _columnWidths = columnWidths.ToDictionary(x => x.Index); - } + private ExcelWidthCollection(ICollection columnWidths, double maxWidth) + { + _maxWidth = ExcelColumnWidth.GetWidthFromTextLength(maxWidth); + _columnWidths = columnWidths.ToDictionary(x => x.Index); + } - internal static ExcelWidthCollection FromProps(ICollection mappings, double? minWidth = null, double maxWidth = 200) - { - var i = 1; - var columnWidths = new List(); + internal static ExcelWidthCollection FromProps(ICollection mappings, double? minWidth = null, double maxWidth = 200) + { + var i = 1; + var columnWidths = new List(); - foreach (var map in mappings) + foreach (var map in mappings) + { + if ((map?.ExcelHidden ?? false) || map?.ExcelColumnWidth != null || minWidth != null) { - if ((map?.ExcelHidden ?? false) || map?.ExcelColumnWidth != null || minWidth != null) - { - var colIndex = map?.ExcelColumnIndex + 1 ?? i; - var hidden = map?.ExcelHidden ?? false; - var width = map?.ExcelColumnWidth ?? minWidth ?? 8.42857143; - - columnWidths.Add(new ExcelColumnWidth { Index = colIndex, Width = width + ExcelColumnWidth.Aptos11Padding, Hidden = hidden}); - } + var colIndex = map?.ExcelColumnIndex + 1 ?? i; + var hidden = map?.ExcelHidden ?? false; + var width = map?.ExcelColumnWidth ?? minWidth ?? 8.42857143; - i++; + columnWidths.Add(new ExcelColumnWidth { Index = colIndex, Width = width + ExcelColumnWidth.Aptos11Padding, Hidden = hidden}); } - return new ExcelWidthCollection(columnWidths, maxWidth); + i++; } - internal void AdjustWidth(int columnIndex, string columnValue) + return new ExcelWidthCollection(columnWidths, maxWidth); + } + + internal void AdjustWidth(int columnIndex, string columnValue) + { + if (!string.IsNullOrEmpty(columnValue) && _columnWidths.TryGetValue(columnIndex, out var currentWidth)) { - if (!string.IsNullOrEmpty(columnValue) && _columnWidths.TryGetValue(columnIndex, out var currentWidth)) - { - var desiredWidth = ExcelColumnWidth.GetWidthFromTextLength(columnValue.Length); - var adjustedWidth = Math.Max(currentWidth.Width, desiredWidth); - currentWidth.Width = Math.Min(_maxWidth, adjustedWidth); - } + var desiredWidth = ExcelColumnWidth.GetWidthFromTextLength(columnValue.Length); + var adjustedWidth = Math.Max(currentWidth.Width, desiredWidth); + currentWidth.Width = Math.Min(_maxWidth, adjustedWidth); } + } - public IEnumerator GetEnumerator() => _columnWidths.Values.GetEnumerator(); - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + public IEnumerator GetEnumerator() => _columnWidths.Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - public int Count => _columnWidths.Count; - } -} + public int Count => _columnWidths.Count; +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/MergeCells.cs b/src/MiniExcel/OpenXml/MergeCells.cs index 51ad3490..ad6055bd 100644 --- a/src/MiniExcel/OpenXml/MergeCells.cs +++ b/src/MiniExcel/OpenXml/MergeCells.cs @@ -1,10 +1,7 @@ -using System.Collections.Generic; +namespace MiniExcelLibs.OpenXml; -namespace MiniExcelLibs.OpenXml +internal class MergeCells { - internal class MergeCells - { - public Dictionary MergesValues { get; set; } = new Dictionary(); - public Dictionary MergesMap { get; set; } = new Dictionary(); - } -} + public Dictionary MergesValues { get; set; } = new Dictionary(); + public Dictionary MergesMap { get; set; } = new Dictionary(); +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/MiniExcelAsyncStreamWriter.cs b/src/MiniExcel/OpenXml/MiniExcelAsyncStreamWriter.cs index b2f8aff1..2d9460bc 100644 --- a/src/MiniExcel/OpenXml/MiniExcelAsyncStreamWriter.cs +++ b/src/MiniExcel/OpenXml/MiniExcelAsyncStreamWriter.cs @@ -2,76 +2,75 @@ // Copyright (c) 2024 Rolls-Royce plc // -namespace MiniExcelLibs.OpenXml -{ - using System; - using System.IO; - using System.Text; - using System.Threading; - using System.Threading.Tasks; +namespace MiniExcelLibs.OpenXml; - internal class MiniExcelAsyncStreamWriter : IDisposable - { - private readonly Stream _stream; - private readonly Encoding _encoding; - private readonly CancellationToken _cancellationToken; - private readonly StreamWriter _streamWriter; - private bool _disposedValue; +using System; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +internal class MiniExcelAsyncStreamWriter : IDisposable +{ + private readonly Stream _stream; + private readonly Encoding _encoding; + private readonly CancellationToken _cancellationToken; + private readonly StreamWriter _streamWriter; + private bool _disposedValue; - public MiniExcelAsyncStreamWriter(Stream stream, Encoding encoding, int bufferSize, CancellationToken cancellationToken) - { - _stream = stream; - _encoding = encoding; - _cancellationToken = cancellationToken; - _streamWriter = new StreamWriter(stream, _encoding, bufferSize); - } - public async Task WriteAsync(string content) - { - _cancellationToken.ThrowIfCancellationRequested(); + public MiniExcelAsyncStreamWriter(Stream stream, Encoding encoding, int bufferSize, CancellationToken cancellationToken) + { + _stream = stream; + _encoding = encoding; + _cancellationToken = cancellationToken; + _streamWriter = new StreamWriter(stream, _encoding, bufferSize); + } + public async Task WriteAsync(string content) + { + _cancellationToken.ThrowIfCancellationRequested(); - if (string.IsNullOrEmpty(content)) - return; - await _streamWriter.WriteAsync(content); - } + if (string.IsNullOrEmpty(content)) + return; + await _streamWriter.WriteAsync(content); + } - public async Task WriteAndFlushAsync(string content) - { - await WriteAsync(content); - return await FlushAsync(); - } + public async Task WriteAndFlushAsync(string content) + { + await WriteAsync(content); + return await FlushAsync(); + } - public async Task WriteWhitespaceAsync(int length) - { - await _streamWriter.WriteAsync(new string(' ', length)); - } + public async Task WriteWhitespaceAsync(int length) + { + await _streamWriter.WriteAsync(new string(' ', length)); + } - public async Task FlushAsync() - { - _cancellationToken.ThrowIfCancellationRequested(); + public async Task FlushAsync() + { + _cancellationToken.ThrowIfCancellationRequested(); - await _streamWriter.FlushAsync(); - return _streamWriter.BaseStream.Position; - } + await _streamWriter.FlushAsync(); + return _streamWriter.BaseStream.Position; + } - public void SetPosition(long position) - { - _streamWriter.BaseStream.Position = position; - } + public void SetPosition(long position) + { + _streamWriter.BaseStream.Position = position; + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - if (!_disposedValue) - { - _streamWriter?.Dispose(); - _disposedValue = true; - } + _streamWriter?.Dispose(); + _disposedValue = true; } + } - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/MiniExcelStreamWriter.cs b/src/MiniExcel/OpenXml/MiniExcelStreamWriter.cs index 4a5f1c58..c05c036d 100644 --- a/src/MiniExcel/OpenXml/MiniExcelStreamWriter.cs +++ b/src/MiniExcel/OpenXml/MiniExcelStreamWriter.cs @@ -1,67 +1,64 @@ -using System; -using System.IO; -using System.Text; +using System.Text; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal class MiniExcelStreamWriter : IDisposable { - internal class MiniExcelStreamWriter : IDisposable - { - private readonly Stream _stream; - private readonly Encoding _encoding; - private readonly StreamWriter _streamWriter; - private bool disposedValue; + private readonly Stream _stream; + private readonly Encoding _encoding; + private readonly StreamWriter _streamWriter; + private bool disposedValue; - public MiniExcelStreamWriter(Stream stream, Encoding encoding, int bufferSize) - { - _stream = stream; - _encoding = encoding; - _streamWriter = new StreamWriter(stream, _encoding, bufferSize); - } - public void Write(string content) - { - if (string.IsNullOrEmpty(content)) - return; + public MiniExcelStreamWriter(Stream stream, Encoding encoding, int bufferSize) + { + _stream = stream; + _encoding = encoding; + _streamWriter = new StreamWriter(stream, _encoding, bufferSize); + } + public void Write(string content) + { + if (string.IsNullOrEmpty(content)) + return; - _streamWriter.Write(content); - } + _streamWriter.Write(content); + } - public long WriteAndFlush(string content) - { - Write(content); - _streamWriter.Flush(); - return _streamWriter.BaseStream.Position; - } + public long WriteAndFlush(string content) + { + Write(content); + _streamWriter.Flush(); + return _streamWriter.BaseStream.Position; + } - public void WriteWhitespace(int length) - { - _streamWriter.Write(new string(' ', length)); - } + public void WriteWhitespace(int length) + { + _streamWriter.Write(new string(' ', length)); + } - public long Flush() - { - _streamWriter.Flush(); - return _streamWriter.BaseStream.Position; - } + public long Flush() + { + _streamWriter.Flush(); + return _streamWriter.BaseStream.Position; + } - public void SetPosition(long position) - { - _streamWriter.BaseStream.Position = position; - } + public void SetPosition(long position) + { + _streamWriter.BaseStream.Position = position; + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!disposedValue) { - if (!disposedValue) - { - _streamWriter?.Dispose(); - disposedValue = true; - } + _streamWriter?.Dispose(); + disposedValue = true; } + } - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Models/DrawingDto.cs b/src/MiniExcel/OpenXml/Models/DrawingDto.cs index 6d8df6cd..05512e61 100644 --- a/src/MiniExcel/OpenXml/Models/DrawingDto.cs +++ b/src/MiniExcel/OpenXml/Models/DrawingDto.cs @@ -1,9 +1,6 @@ -using System; +namespace MiniExcelLibs.OpenXml.Models; -namespace MiniExcelLibs.OpenXml.Models +internal class DrawingDto { - internal class DrawingDto - { - internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; - } + internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Models/ExcelRange.cs b/src/MiniExcel/OpenXml/Models/ExcelRange.cs index a8f784dd..c4fa6748 100644 --- a/src/MiniExcel/OpenXml/Models/ExcelRange.cs +++ b/src/MiniExcel/OpenXml/Models/ExcelRange.cs @@ -1,36 +1,33 @@ -using System; +namespace MiniExcelLibs.OpenXml.Models; -namespace MiniExcelLibs.OpenXml.Models +public class ExcelRangeElement { - public class ExcelRangeElement + internal ExcelRangeElement(int startIndex, int endIndex) { - internal ExcelRangeElement(int startIndex, int endIndex) - { - if (startIndex > endIndex) - throw new ArgumentException("StartIndex value cannot be greater than EndIndex value."); + if (startIndex > endIndex) + throw new ArgumentException("StartIndex value cannot be greater than EndIndex value."); - StartIndex = startIndex; - EndIndex = endIndex; - } + StartIndex = startIndex; + EndIndex = endIndex; + } - public int StartIndex { get; } - public int EndIndex { get; } + public int StartIndex { get; } + public int EndIndex { get; } - public int Count => EndIndex - StartIndex + 1; - } + public int Count => EndIndex - StartIndex + 1; +} - public class ExcelRange +public class ExcelRange +{ + public ExcelRange(int maxRow, int maxColumn) { - public ExcelRange(int maxRow, int maxColumn) - { - Rows = new ExcelRangeElement(1, maxRow); - Columns = new ExcelRangeElement(1, maxColumn); - } + Rows = new ExcelRangeElement(1, maxRow); + Columns = new ExcelRangeElement(1, maxColumn); + } - public string StartCell { get; internal set; } - public string EndCell { get; internal set; } + public string StartCell { get; internal set; } + public string EndCell { get; internal set; } - public ExcelRangeElement Rows { get; } - public ExcelRangeElement Columns { get; } - } + public ExcelRangeElement Rows { get; } + public ExcelRangeElement Columns { get; } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Models/FileDto.cs b/src/MiniExcel/OpenXml/Models/FileDto.cs index 57e4feb8..68cbafda 100644 --- a/src/MiniExcel/OpenXml/Models/FileDto.cs +++ b/src/MiniExcel/OpenXml/Models/FileDto.cs @@ -1,17 +1,14 @@ -using System; +namespace MiniExcelLibs.OpenXml.Models; -namespace MiniExcelLibs.OpenXml.Models +internal class FileDto { - internal class FileDto - { - internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; - internal string Extension { get; set; } - internal string Path => $"xl/media/{ID}.{Extension}"; - internal string Path2 => $"/xl/media/{ID}.{Extension}"; - internal byte[] Byte { get; set; } - internal int RowIndex { get; set; } - internal int CellIndex { get; set; } - internal bool IsImage { get; set; } - internal int SheetId { get; set; } - } + internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; + internal string Extension { get; set; } + internal string Path => $"xl/media/{ID}.{Extension}"; + internal string Path2 => $"/xl/media/{ID}.{Extension}"; + internal byte[] Byte { get; set; } + internal int RowIndex { get; set; } + internal int CellIndex { get; set; } + internal bool IsImage { get; set; } + internal int SheetId { get; set; } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Models/SheetDto.cs b/src/MiniExcel/OpenXml/Models/SheetDto.cs index c75c69f6..045c83b4 100644 --- a/src/MiniExcel/OpenXml/Models/SheetDto.cs +++ b/src/MiniExcel/OpenXml/Models/SheetDto.cs @@ -1,14 +1,11 @@ -using System; +namespace MiniExcelLibs.OpenXml.Models; -namespace MiniExcelLibs.OpenXml.Models +internal class SheetDto { - internal class SheetDto - { - internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; - internal string Name { get; set; } - internal int SheetIdx { get; set; } - internal string Path => $"xl/worksheets/sheet{SheetIdx}.xml"; + internal string ID { get; set; } = $"R{Guid.NewGuid():N}"; + internal string Name { get; set; } + internal int SheetIdx { get; set; } + internal string Path => $"xl/worksheets/sheet{SheetIdx}.xml"; - internal string State { get; set; } - } + internal string State { get; set; } } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs b/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs index 18355809..3ae71412 100644 --- a/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs +++ b/src/MiniExcel/OpenXml/OpenXmlConfiguration.cs @@ -1,42 +1,40 @@ -using System.IO; using MiniExcelLibs.Attributes; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +public class OpenXmlConfiguration : Configuration { - public class OpenXmlConfiguration : Configuration - { - internal static readonly OpenXmlConfiguration DefaultConfig = new OpenXmlConfiguration(); - public bool FillMergedCells { get; set; } - public TableStyles TableStyles { get; set; } = TableStyles.Default; - public bool AutoFilter { get; set; } = true; - public bool RightToLeft { get; set; } = false; - public int FreezeRowCount { get; set; } = 1; - public int FreezeColumnCount { get; set; } = 0; - public bool EnableConvertByteArray { get; set; } = true; - public bool IgnoreTemplateParameterMissing { get; set; } = true; - public bool EnableWriteNullValueCell { get; set; } = true; - public bool WriteEmptyStringAsNull { get; set; } = false; - public bool TrimColumnNames { get; set; } = true; - public bool IgnoreEmptyRows { get; set; } = false; - public bool EnableSharedStringCache { get; set; } = true; - public long SharedStringCacheSize { get; set; } = 5 * 1024 * 1024; + internal static readonly OpenXmlConfiguration DefaultConfig = new OpenXmlConfiguration(); + public bool FillMergedCells { get; set; } + public TableStyles TableStyles { get; set; } = TableStyles.Default; + public bool AutoFilter { get; set; } = true; + public bool RightToLeft { get; set; } = false; + public int FreezeRowCount { get; set; } = 1; + public int FreezeColumnCount { get; set; } = 0; + public bool EnableConvertByteArray { get; set; } = true; + public bool IgnoreTemplateParameterMissing { get; set; } = true; + public bool EnableWriteNullValueCell { get; set; } = true; + public bool WriteEmptyStringAsNull { get; set; } = false; + public bool TrimColumnNames { get; set; } = true; + public bool IgnoreEmptyRows { get; set; } = false; + public bool EnableSharedStringCache { get; set; } = true; + public long SharedStringCacheSize { get; set; } = 5 * 1024 * 1024; - /// - /// The directory where the shared strings cache files are stored. - /// It defaults to the system's temporary folder. - /// - public string SharedStringCachePath { get; set; } = Path.GetTempPath(); + /// + /// The directory where the shared strings cache files are stored. + /// It defaults to the system's temporary folder. + /// + public string SharedStringCachePath { get; set; } = Path.GetTempPath(); - public OpenXmlStyleOptions StyleOptions { get; set; } = new OpenXmlStyleOptions(); - public DynamicExcelSheet[] DynamicSheets { get; set; } - public bool EnableWriteFilePath{ get; set; } = true; - /// - /// Calculate column widths automatically from each column value. - /// - public bool EnableAutoWidth { get; set; } + public OpenXmlStyleOptions StyleOptions { get; set; } = new OpenXmlStyleOptions(); + public DynamicExcelSheet[] DynamicSheets { get; set; } + public bool EnableWriteFilePath{ get; set; } = true; + /// + /// Calculate column widths automatically from each column value. + /// + public bool EnableAutoWidth { get; set; } - public double MinWidth { get; set; } = 8.42857143; + public double MinWidth { get; set; } = 8.42857143; - public double MaxWidth { get; set; } = 200; - } + public double MaxWidth { get; set; } = 200; } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/OpenXmlStyleOptions.cs b/src/MiniExcel/OpenXml/OpenXmlStyleOptions.cs index 793525c7..99a1f26b 100644 --- a/src/MiniExcel/OpenXml/OpenXmlStyleOptions.cs +++ b/src/MiniExcel/OpenXml/OpenXmlStyleOptions.cs @@ -1,38 +1,37 @@ using System.Drawing; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +public class OpenXmlStyleOptions { - public class OpenXmlStyleOptions - { - public OpenXmlHeaderStyle HeaderStyle { get; set; } - public bool WrapCellContents { get; set; } - public HorizontalCellAlignment HorizontalAlignment { get; set; } - public VerticalCellAlignment VerticalAlignment { get; set; } - } + public OpenXmlHeaderStyle HeaderStyle { get; set; } + public bool WrapCellContents { get; set; } + public HorizontalCellAlignment HorizontalAlignment { get; set; } + public VerticalCellAlignment VerticalAlignment { get; set; } +} - public class OpenXmlHeaderStyle - { - /// - /// Whether to wrap the content of the header - /// - public bool WrapText { get; set; } +public class OpenXmlHeaderStyle +{ + /// + /// Whether to wrap the content of the header + /// + public bool WrapText { get; set; } - /// - /// The RGB background color in the filtered state - /// - public Color BackgroundColor { get; set; } = Color.FromArgb(0x284472C4); + /// + /// The RGB background color in the filtered state + /// + public Color BackgroundColor { get; set; } = Color.FromArgb(0x284472C4); - /// - /// Horizontal alignment - /// - public HorizontalCellAlignment HorizontalAlignment { get; set; } = HorizontalCellAlignment.Left; + /// + /// Horizontal alignment + /// + public HorizontalCellAlignment HorizontalAlignment { get; set; } = HorizontalCellAlignment.Left; - /// - /// Vertical alignment - /// - public VerticalCellAlignment VerticalAlignment { get; set; } = VerticalCellAlignment.Bottom; - } - - public enum HorizontalCellAlignment { Left, Center, Right } - public enum VerticalCellAlignment { Bottom, Center, Top } + /// + /// Vertical alignment + /// + public VerticalCellAlignment VerticalAlignment { get; set; } = VerticalCellAlignment.Bottom; } + +public enum HorizontalCellAlignment { Left, Center, Right } +public enum VerticalCellAlignment { Bottom, Center, Top } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/SharedStringsDiskCache.cs b/src/MiniExcel/OpenXml/SharedStringsDiskCache.cs index b35f66a2..55c421a1 100644 --- a/src/MiniExcel/OpenXml/SharedStringsDiskCache.cs +++ b/src/MiniExcel/OpenXml/SharedStringsDiskCache.cs @@ -1,161 +1,157 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; +using System.Collections; using System.Text; -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +internal class SharedStringsDiskCache : IDictionary, IDisposable { - internal class SharedStringsDiskCache : IDictionary, IDisposable - { - private static readonly Encoding _encoding = new UTF8Encoding(true); + private static readonly Encoding _encoding = new UTF8Encoding(true); - private readonly FileStream _positionFs; - private readonly FileStream _lengthFs; - private readonly FileStream _valueFs; - private bool _disposedValue; + private readonly FileStream _positionFs; + private readonly FileStream _lengthFs; + private readonly FileStream _valueFs; + private bool _disposedValue; - private long _maxIndx = -1; - public int Count => checked((int)(_maxIndx + 1)); - public string this[int key] { get => GetValue(key); set => Add(key, value); } - public bool ContainsKey(int key) - { - return key <= _maxIndx; - } + private long _maxIndx = -1; + public int Count => checked((int)(_maxIndx + 1)); + public string this[int key] { get => GetValue(key); set => Add(key, value); } + public bool ContainsKey(int key) + { + return key <= _maxIndx; + } - public SharedStringsDiskCache(string sharedStringsCacheDir) - { - if (string.IsNullOrWhiteSpace(sharedStringsCacheDir) || !Directory.Exists(sharedStringsCacheDir)) - throw new DirectoryNotFoundException($"\"{sharedStringsCacheDir}\" is not a valid directory for the shared strings cache."); + public SharedStringsDiskCache(string sharedStringsCacheDir) + { + if (string.IsNullOrWhiteSpace(sharedStringsCacheDir) || !Directory.Exists(sharedStringsCacheDir)) + throw new DirectoryNotFoundException($"\"{sharedStringsCacheDir}\" is not a valid directory for the shared strings cache."); - var prefix = $"{Path.GetRandomFileName()}_miniexcel"; - _positionFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_position"), FileMode.OpenOrCreate); - _lengthFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_length"), FileMode.OpenOrCreate); - _valueFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_data"), FileMode.OpenOrCreate); - } + var prefix = $"{Path.GetRandomFileName()}_miniexcel"; + _positionFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_position"), FileMode.OpenOrCreate); + _lengthFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_length"), FileMode.OpenOrCreate); + _valueFs = new FileStream(Path.Combine(sharedStringsCacheDir, $"{prefix}_data"), FileMode.OpenOrCreate); + } - // index must start with 0-N - internal void Add(int index, string value) - { - if (index > _maxIndx) - _maxIndx = index; + // index must start with 0-N + internal void Add(int index, string value) + { + if (index > _maxIndx) + _maxIndx = index; - var valueBs = _encoding.GetBytes(value); - if (value.Length > 32767) //check info length, becasue cell string max length is 47483647 - throw new ArgumentOutOfRangeException("", "Excel one cell max length is 32,767 characters"); + var valueBs = _encoding.GetBytes(value); + if (value.Length > 32767) //check info length, becasue cell string max length is 47483647 + throw new ArgumentOutOfRangeException("", "Excel one cell max length is 32,767 characters"); - _positionFs.Write(BitConverter.GetBytes(_valueFs.Position), 0, 4); - _lengthFs.Write(BitConverter.GetBytes(valueBs.Length), 0, 4); - _valueFs.Write(valueBs, 0, valueBs.Length); - } + _positionFs.Write(BitConverter.GetBytes(_valueFs.Position), 0, 4); + _lengthFs.Write(BitConverter.GetBytes(valueBs.Length), 0, 4); + _valueFs.Write(valueBs, 0, valueBs.Length); + } - private string GetValue(int index) - { - _positionFs.Position = index * 4; - var bytes = new byte[4]; - _ = _positionFs.Read(bytes, 0, 4); - var position = BitConverter.ToInt32(bytes, 0); + private string GetValue(int index) + { + _positionFs.Position = index * 4; + var bytes = new byte[4]; + _ = _positionFs.Read(bytes, 0, 4); + var position = BitConverter.ToInt32(bytes, 0); - bytes = new byte[4]; - _lengthFs.Position = index * 4; - _ = _lengthFs.Read(bytes, 0, 4); - var length = BitConverter.ToInt32(bytes, 0); + bytes = new byte[4]; + _lengthFs.Position = index * 4; + _ = _lengthFs.Read(bytes, 0, 4); + var length = BitConverter.ToInt32(bytes, 0); - bytes = new byte[length]; - _valueFs.Position = position; - _ = _valueFs.Read(bytes, 0, length); + bytes = new byte[length]; + _valueFs.Position = position; + _ = _valueFs.Read(bytes, 0, length); - return _encoding.GetString(bytes); - } + return _encoding.GetString(bytes); + } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) { - if (!_disposedValue) + if (disposing) { - if (disposing) - { - // TODO: dispose managed state (managed objects) - } + // TODO: dispose managed state (managed objects) + } - _positionFs.Dispose(); - if (File.Exists(_positionFs.Name)) - File.Delete(_positionFs.Name); + _positionFs.Dispose(); + if (File.Exists(_positionFs.Name)) + File.Delete(_positionFs.Name); - _lengthFs.Dispose(); - if (File.Exists(_lengthFs.Name)) - File.Delete(_lengthFs.Name); + _lengthFs.Dispose(); + if (File.Exists(_lengthFs.Name)) + File.Delete(_lengthFs.Name); - _valueFs.Dispose(); - if (File.Exists(_valueFs.Name)) - File.Delete(_valueFs.Name); + _valueFs.Dispose(); + if (File.Exists(_valueFs.Name)) + File.Delete(_valueFs.Name); - _disposedValue = true; - } + _disposedValue = true; } + } - ~SharedStringsDiskCache() - { - Dispose(disposing: false); - } + ~SharedStringsDiskCache() + { + Dispose(disposing: false); + } - public void Dispose() - { - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - public ICollection Keys => throw new NotImplementedException(); - public ICollection Values => throw new NotImplementedException(); - public bool IsReadOnly => throw new NotImplementedException(); - public bool Remove(int key) - { - throw new NotImplementedException(); - } + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + public ICollection Keys => throw new NotImplementedException(); + public ICollection Values => throw new NotImplementedException(); + public bool IsReadOnly => throw new NotImplementedException(); + public bool Remove(int key) + { + throw new NotImplementedException(); + } - public bool TryGetValue(int key, out string value) - { - throw new NotImplementedException(); - } + public bool TryGetValue(int key, out string value) + { + throw new NotImplementedException(); + } - public void Add(KeyValuePair item) - { - throw new NotImplementedException(); - } + public void Add(KeyValuePair item) + { + throw new NotImplementedException(); + } - public void Clear() - { - throw new NotImplementedException(); - } + public void Clear() + { + throw new NotImplementedException(); + } - public bool Contains(KeyValuePair item) - { - throw new NotImplementedException(); - } + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - throw new NotImplementedException(); - } + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } - public bool Remove(KeyValuePair item) - { - throw new NotImplementedException(); - } + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } - public IEnumerator> GetEnumerator() - { - for (int i = 0; i <= _maxIndx; i++) - yield return new KeyValuePair(i, this[i]); - } + public IEnumerator> GetEnumerator() + { + for (int i = 0; i <= _maxIndx; i++) + yield return new KeyValuePair(i, this[i]); + } - IEnumerator IEnumerable.GetEnumerator() - { - for (int i = 0; i <= _maxIndx; i++) - yield return this[i]; - } + IEnumerator IEnumerable.GetEnumerator() + { + for (int i = 0; i <= _maxIndx; i++) + yield return this[i]; + } - void IDictionary.Add(int key, string value) - { - throw new NotImplementedException(); - } + void IDictionary.Add(int key, string value) + { + throw new NotImplementedException(); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/SheetInfo.cs b/src/MiniExcel/OpenXml/SheetInfo.cs index 11606ddd..76a413b0 100644 --- a/src/MiniExcel/OpenXml/SheetInfo.cs +++ b/src/MiniExcel/OpenXml/SheetInfo.cs @@ -1,37 +1,36 @@ -namespace MiniExcelLibs.OpenXml +namespace MiniExcelLibs.OpenXml; + +public class SheetInfo { - public class SheetInfo + public SheetInfo(uint id, uint index, string name, SheetState sheetState, bool active) { - public SheetInfo(uint id, uint index, string name, SheetState sheetState, bool active) - { - Id = id; - Index = index; - Name = name; - State = sheetState; - Active = active; - } - - /// - /// Internal sheet id - depends on the order in which the sheet is added - /// - public uint Id { get; } - /// - /// Next sheet index - numbered from 0 - /// - public uint Index { get; } - /// - /// Sheet name - /// - public string Name { get; } - /// - /// Sheet visibility state - /// - public SheetState State { get; } - /// - /// Indicates whether the sheet is active - /// - public bool Active { get; } + Id = id; + Index = index; + Name = name; + State = sheetState; + Active = active; } - public enum SheetState { Visible, Hidden, VeryHidden } + /// + /// Internal sheet id - depends on the order in which the sheet is added + /// + public uint Id { get; } + /// + /// Next sheet index - numbered from 0 + /// + public uint Index { get; } + /// + /// Sheet name + /// + public string Name { get; } + /// + /// Sheet visibility state + /// + public SheetState State { get; } + /// + /// Indicates whether the sheet is active + /// + public bool Active { get; } } + +public enum SheetState { Visible, Hidden, VeryHidden } \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/SheetRecord.cs b/src/MiniExcel/OpenXml/SheetRecord.cs index e301b932..8066c38e 100644 --- a/src/MiniExcel/OpenXml/SheetRecord.cs +++ b/src/MiniExcel/OpenXml/SheetRecord.cs @@ -1,41 +1,38 @@ -using System; +namespace MiniExcelLibs.OpenXml; -namespace MiniExcelLibs.OpenXml +internal sealed class SheetRecord { - internal sealed class SheetRecord + public SheetRecord(string name, string state, uint id, string rid, bool active) { - public SheetRecord(string name, string state, uint id, string rid, bool active) - { - Name = name; - State = state; - Id = id; - Rid = rid; - Active = active; - } + Name = name; + State = state; + Id = id; + Rid = rid; + Active = active; + } - public string Name { get; } + public string Name { get; } - public string State { get; set; } + public string State { get; set; } - public uint Id { get; } + public uint Id { get; } - public string Rid { get; set; } + public string Rid { get; set; } - public string Path { get; set; } + public string Path { get; set; } - public bool Active { get; } + public bool Active { get; } - public SheetInfo ToSheetInfo(uint index) + public SheetInfo ToSheetInfo(uint index) + { + if (string.IsNullOrEmpty(State)) + { + return new SheetInfo(Id, index, Name, SheetState.Visible, Active); + } + if (Enum.TryParse(State, true, out SheetState stateEnum)) { - if (string.IsNullOrEmpty(State)) - { - return new SheetInfo(Id, index, Name, SheetState.Visible, Active); - } - if (Enum.TryParse(State, true, out SheetState stateEnum)) - { - return new SheetInfo(Id, index, Name, stateEnum, Active); - } - throw new ArgumentException($"Unable to parse sheet state. Sheet name: {Name}"); + return new SheetInfo(Id, index, Name, stateEnum, Active); } + throw new ArgumentException($"Unable to parse sheet state. Sheet name: {Name}"); } -} +} \ No newline at end of file diff --git a/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs b/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs index eaa4dd54..bbbc9ed7 100644 --- a/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs +++ b/src/MiniExcel/OpenXml/Styles/DefaultSheetStyleBuilder.cs @@ -1,760 +1,848 @@ using System.Drawing; -using System.Threading.Tasks; -namespace MiniExcelLibs.OpenXml.Styles +namespace MiniExcelLibs.OpenXml.Styles; + +internal class DefaultSheetStyleBuilder : SheetStyleBuilderBase { - internal class DefaultSheetStyleBuilder : SheetStyleBuilderBase + private static readonly SheetStyleElementInfos GenerateElementInfos = new SheetStyleElementInfos { - private static readonly SheetStyleElementInfos GenerateElementInfos = new SheetStyleElementInfos - { - NumFmtCount = 0,//The default NumFmt number is 0, but there will be NumFmt dynamically generated based on ColumnsToApply - FontCount = 2, - FillCount = 3, - BorderCount = 2, - CellStyleXfCount = 3, - CellXfCount = 5 - }; - - private static readonly Color DefaultBackgroundColor = Color.FromArgb(0x284472C4); - private const HorizontalCellAlignment DefaultHorizontalAlignment = HorizontalCellAlignment.Left; - private const VerticalCellAlignment DefaultVerticalAlignment = VerticalCellAlignment.Bottom; - - private readonly SheetStyleBuildContext _context; - private readonly OpenXmlStyleOptions _styleOptions; - - public DefaultSheetStyleBuilder(SheetStyleBuildContext context, OpenXmlStyleOptions styleOptions) : base(context) - { - _context = context; - _styleOptions = styleOptions; - } - - protected override SheetStyleElementInfos GetGenerateElementInfos() - { - return GenerateElementInfos; - } - - protected override void GenerateNumFmt() - { - const int numFmtIndex = 166; + NumFmtCount = 0,//The default NumFmt number is 0, but there will be NumFmt dynamically generated based on ColumnsToApply + FontCount = 2, + FillCount = 3, + BorderCount = 2, + CellStyleXfCount = 3, + CellXfCount = 5 + }; + + private static readonly Color DefaultBackgroundColor = Color.FromArgb(0x284472C4); + private const HorizontalCellAlignment DefaultHorizontalAlignment = HorizontalCellAlignment.Left; + private const VerticalCellAlignment DefaultVerticalAlignment = VerticalCellAlignment.Bottom; + + private readonly SheetStyleBuildContext _context; + private readonly OpenXmlStyleOptions _styleOptions; + + public DefaultSheetStyleBuilder(SheetStyleBuildContext context, OpenXmlStyleOptions styleOptions) : base(context) + { + _context = context; + _styleOptions = styleOptions; + } - var index = 0; - foreach (var item in _context.ColumnsToApply) - { - 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(); + index++; /* - * - * - * - * - * - * - * + * - * - * - * - * - * - * - */ - 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(); + index++; /* - * - * - * - * - * - * - * + * - * - * - */ - _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(); + protected override void GenerateFont() + { + /* + * + * + * + * + * + * + * + */ + _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(); + } - /* - * - * - * - */ - _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(); + 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(); + } - /* - * - * - * - * - * - */ - _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); + 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); + + var bgColor = _styleOptions.HeaderStyle?.BackgroundColor ?? DefaultBackgroundColor; + var hexBgColor = $"{bgColor.A:X2}{bgColor.R:X2}{bgColor.G:X2}{bgColor.B:X2}"; + _context.NewXmlWriter.WriteAttributeString(null, "rgb", null, hexBgColor); + + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + _context.NewXmlWriter.WriteEndElement(); + } - var bgColor = _styleOptions.HeaderStyle?.BackgroundColor ?? DefaultBackgroundColor; - var hexBgColor = $"{bgColor.A:X2}{bgColor.R:X2}{bgColor.G:X2}{bgColor.B:X2}"; - _context.NewXmlWriter.WriteAttributeString(null, "rgb", null, hexBgColor); + 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); + + var bgColor = _styleOptions.HeaderStyle?.BackgroundColor ?? DefaultBackgroundColor; + var hexBgColor = $"{bgColor.A:X2}{bgColor.R:X2}{bgColor.G:X2}{bgColor.B:X2}"; + await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, hexBgColor).ConfigureAwait(false); - _context.NewXmlWriter.WriteEndElement(); - _context.NewXmlWriter.WriteEndElement(); - _context.NewXmlWriter.WriteEndElement(); - } + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + await _context.NewXmlWriter.WriteEndElementAsync(); + } - 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(); + 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(); + } - /* - * - * - * - */ - 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(); + 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(); + } - /* - * - * - * - * - * - */ - 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); - - var bgColor = _styleOptions.HeaderStyle?.BackgroundColor ?? DefaultBackgroundColor; - var hexBgColor = $"{bgColor.A:X2}{bgColor.R:X2}{bgColor.G:X2}{bgColor.B:X2}"; - await _context.NewXmlWriter.WriteAttributeStringAsync(null, "rgb", null, hexBgColor).ConfigureAwait(false); + 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(); + } - await _context.NewXmlWriter.WriteEndElementAsync(); - await _context.NewXmlWriter.WriteEndElementAsync(); - await _context.NewXmlWriter.WriteEndElementAsync(); - } + 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 GenerateBorder() + 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); + + var horizontalAlignment = _styleOptions.HeaderStyle?.HorizontalAlignment ?? DefaultHorizontalAlignment; + var horizontalAlignmentStr = horizontalAlignment.ToString().ToLowerInvariant(); + _context.NewXmlWriter.WriteAttributeString(null, "horizontal", null, horizontalAlignmentStr); + + var verticalAlignment = _styleOptions.HeaderStyle?.VerticalAlignment ?? DefaultVerticalAlignment; + var verticalAlignmentStr = verticalAlignment.ToString().ToLowerInvariant(); + _context.NewXmlWriter.WriteAttributeString(null, "vertical", null, verticalAlignmentStr); + + var wrapHeader = (_styleOptions.HeaderStyle?.WrapText ?? false) ? "1" : "0"; + _context.NewXmlWriter.WriteAttributeString(null, "wrapText", null, wrapHeader); + + _context.NewXmlWriter.WriteAttributeString("textRotation", "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); + string style1HorizontalAlignment; + switch (_styleOptions.HorizontalAlignment) { - /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - _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(); + case HorizontalCellAlignment.Center: + style1HorizontalAlignment = "center"; + break; + case HorizontalCellAlignment.Right: + style1HorizontalAlignment = "right"; + break; + default: + style1HorizontalAlignment = "general"; + break; } - protected override async Task GenerateBorderAsync() + string style1VerticalAlignment; + switch (_styleOptions.VerticalAlignment) { - /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - 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(); + case VerticalCellAlignment.Top: + style1VerticalAlignment = "top"; + break; + case VerticalCellAlignment.Center: + style1VerticalAlignment = "center"; + break; + default: + style1VerticalAlignment = "bottom"; + break; } - protected override void GenerateCellStyleXf() + _context.NewXmlWriter.WriteAttributeString("horizontal", style1HorizontalAlignment); + _context.NewXmlWriter.WriteAttributeString("vertical", style1VerticalAlignment); + _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); + + var wrapContent = _styleOptions.WrapCellContents ? "1" : "0"; + _context.NewXmlWriter.WriteAttributeString(null, "wrapText", null, wrapContent); + + _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); + string style2HorizontalAlignment; + switch (_styleOptions.HorizontalAlignment) { - /* - * - * - */ - _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(); + case HorizontalCellAlignment.Center: + style2HorizontalAlignment = "center"; + break; + case HorizontalCellAlignment.Right: + style2HorizontalAlignment = "right"; + break; + default: + style2HorizontalAlignment = "general"; + break; } - protected override async Task GenerateCellStyleXfAsync() + string style2VerticalAlignment; + switch (_styleOptions.VerticalAlignment) { - /* - * - * - */ - 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(); + case VerticalCellAlignment.Top: + style2VerticalAlignment = "top"; + break; + case VerticalCellAlignment.Center: + style2VerticalAlignment = "center"; + break; + default: + style2VerticalAlignment = "bottom"; + break; } - protected override void GenerateCellXf() + _context.NewXmlWriter.WriteAttributeString("horizontal", style2HorizontalAlignment); + _context.NewXmlWriter.WriteAttributeString("vertical", style2VerticalAlignment); + _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) { - /* - * - */ - _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); - - var horizontalAlignment = _styleOptions.HeaderStyle?.HorizontalAlignment ?? DefaultHorizontalAlignment; - var horizontalAlignmentStr = horizontalAlignment.ToString().ToLowerInvariant(); - _context.NewXmlWriter.WriteAttributeString(null, "horizontal", null, horizontalAlignmentStr); - - var verticalAlignment = _styleOptions.HeaderStyle?.VerticalAlignment ?? DefaultVerticalAlignment; - var verticalAlignmentStr = verticalAlignment.ToString().ToLowerInvariant(); - _context.NewXmlWriter.WriteAttributeString(null, "vertical", null, verticalAlignmentStr); - - var wrapHeader = (_styleOptions.HeaderStyle?.WrapText ?? false) ? "1" : "0"; - _context.NewXmlWriter.WriteAttributeString(null, "wrapText", null, wrapHeader); - - _context.NewXmlWriter.WriteAttributeString("textRotation", "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); - string style1HorizontalAlignment; - switch (_styleOptions.HorizontalAlignment) - { - case HorizontalCellAlignment.Center: - style1HorizontalAlignment = "center"; - break; - case HorizontalCellAlignment.Right: - style1HorizontalAlignment = "right"; - break; - default: - style1HorizontalAlignment = "general"; - break; - } - - string style1VerticalAlignment; - switch (_styleOptions.VerticalAlignment) - { - case VerticalCellAlignment.Top: - style1VerticalAlignment = "top"; - break; - case VerticalCellAlignment.Center: - style1VerticalAlignment = "center"; - break; - default: - style1VerticalAlignment = "bottom"; - break; - } - - _context.NewXmlWriter.WriteAttributeString("horizontal", style1HorizontalAlignment); - _context.NewXmlWriter.WriteAttributeString("vertical", style1VerticalAlignment); - _context.NewXmlWriter.WriteAttributeString("textRotation", "0"); - - var wrapContent = _styleOptions.WrapCellContents ? "1" : "0"; - _context.NewXmlWriter.WriteAttributeString(null, "wrapText", null, wrapContent); - - _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(); + index++; /* - * - * - *