diff --git a/src/MiniExcel.OpenXml/Constants/ExcelXml.cs b/src/MiniExcel.OpenXml/Constants/ExcelXml.cs index 7d6a500e..a6b255d7 100644 --- a/src/MiniExcel.OpenXml/Constants/ExcelXml.cs +++ b/src/MiniExcel.OpenXml/Constants/ExcelXml.cs @@ -78,7 +78,7 @@ internal static string SharedStrings(Dictionary sharedStrings) { sb.Append("{XmlHelper.EncodeXml(text)}"); diff --git a/src/MiniExcel.OpenXml/Helpers/XmlHelper.cs b/src/MiniExcel.OpenXml/Helpers/XmlHelper.cs index 48baaadc..ecd01522 100644 --- a/src/MiniExcel.OpenXml/Helpers/XmlHelper.cs +++ b/src/MiniExcel.OpenXml/Helpers/XmlHelper.cs @@ -42,6 +42,12 @@ internal static partial class XmlHelper return XmlConvert.DecodeName(decodeStr); } + /// + /// Excel drops leading and trailing blanks (spaces, tabs, line breaks) from a <t> without xml:space="preserve". + /// + public static bool HasLeadingOrTrailingWhiteSpace(string? text) + => text is { Length: > 0 } && (char.IsWhiteSpace(text[0]) || char.IsWhiteSpace(text[text.Length - 1])); + public static string MinifyXml(string xml) => xml .Replace("\r", "") .Replace("\n", "") diff --git a/src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs b/src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs index 2c7aeaff..a63de2cf 100644 --- a/src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs +++ b/src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs @@ -482,7 +482,7 @@ private async Task WriteCellAsync(MiniExcelStreamWriter writer, int rowIndex, in } /*Prefix and suffix blank space will lost after SaveAs #294*/ - var preserveSpace = cellValue.StartsWith(" ") || cellValue.EndsWith(" "); + var preserveSpace = XmlHelper.HasLeadingOrTrailingWhiteSpace(cellValue); await writer.WriteAsync(WorksheetXml.Cell(columnReference, dataType, styleIndex, cellValue, preserveSpace: preserveSpace, columnType: columnType), cancellationToken).ConfigureAwait(false); } diff --git a/tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGithubIssuesTests.cs b/tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGithubIssuesTests.cs index d6b3ec1c..ce2a4e34 100644 --- a/tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGithubIssuesTests.cs +++ b/tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGithubIssuesTests.cs @@ -1538,6 +1538,25 @@ public void TestIssue294() } } + /// A leading or trailing tab or line break is a blank too, inline and shared - https://github.com/mini-software/MiniExcel/issues/294 + [Theory] + [InlineData("\tJack", StringStorageMode.Inline, true)] + [InlineData("Jack\n", StringStorageMode.Inline, true)] + [InlineData("Ja\tck", StringStorageMode.Inline, false)] + [InlineData("\tJack", StringStorageMode.Shared, true)] + [InlineData("Jack\n", StringStorageMode.Shared, true)] + [InlineData("Ja\tck", StringStorageMode.Shared, false)] + public void TestIssue294WhiteSpace(string name, StringStorageMode mode, bool preserved) + { + using var path = AutoDeletingPath.Create(); + var value = new[] { new { Name = name } }; + _excelExporter.Export(path.ToString(), value, configuration: new OpenXmlConfiguration { StringStorageMode = mode }); + + var part = mode == StringStorageMode.Shared ? "xl/sharedStrings.xml" : "xl/worksheets/sheet1.xml"; + var xml = SheetHelper.GetZipFileContent(path.ToString(), part); + Assert.Contains(preserved ? $"{name}" : $"{name}", xml); + } + /// Prefix and suffix blank space are hidden by Excel after FillTemplate - https://github.com/mini-software/MiniExcel/issues/295 [Fact] public void TestIssue295()