Skip to content

Commit d1c15b0

Browse files
ybai08michelebastione
authored andcommitted
Keep leading and trailing blanks of template values visible in Excel (#295)
FillTemplate wrote inline strings without xml:space="preserve", so Excel dropped leading and trailing blanks from filled values. Mark a <t> that starts or ends with white space, or that still holds a list placeholder whose value is filled in later, the way the exporter already does (#294).
1 parent e769d21 commit d1c15b0

3 files changed

Lines changed: 83 additions & 1 deletion

File tree

‎src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ private async Task GenerateSheetByUpdateModeAsync(ZipArchiveEntry sheetZipEntry,
5454
InjectSharedStrings(sharedStrings, rows);
5555
GetMergeCells(worksheet);
5656
UpdateDimensionAndGetRowsInfo(inputMaps, worksheet, rows, !mergeCells);
57+
PreserveInlineStringSpaces(rows);
5758

5859
#if NET
5960
var writer = XmlWriter.Create(stream, DocXmlWriterSettings);
@@ -90,6 +91,7 @@ private async Task GenerateSheetByCreateModeAsync(ZipArchiveEntry templateSheetZ
9091
InjectSharedStrings(sharedStrings, rows);
9192
GetMergeCells(worksheet);
9293
UpdateDimensionAndGetRowsInfo(inputMaps, worksheet, rows, !mergeCells);
94+
PreserveInlineStringSpaces(rows);
9395

9496
#if NET
9597
var writer = XmlWriter.Create(outputZipSheetEntryStream, DocXmlWriterSettings);
@@ -1068,6 +1070,23 @@ private static void InjectSharedStrings(IDictionary<int, string> sharedStrings,
10681070
}
10691071
}
10701072

1073+
// Excel drops leading and trailing blanks from a <t> without xml:space="preserve" (https://github.com/mini-software/MiniExcel/issues/295).
1074+
// Runs after the single values are filled in; a <t> still holding a placeholder gets its value later, so it is kept as well.
1075+
private static void PreserveInlineStringSpaces(IEnumerable<XElement> rows)
1076+
{
1077+
foreach (var row in rows)
1078+
{
1079+
foreach (var cell in row.Elements(SpreadsheetNs + "c"))
1080+
{
1081+
if (cell.Element(SpreadsheetNs + "is")?.Element(SpreadsheetNs + "t") is not { Value: { Length: > 0 } text } t)
1082+
continue;
1083+
1084+
if (char.IsWhiteSpace(text[0]) || char.IsWhiteSpace(text[text.Length - 1]) || text.Contains("{{"))
1085+
t.SetAttributeValue(XNamespace.Xml + "space", "preserve");
1086+
}
1087+
}
1088+
}
1089+
10711090
private static void SetCellType(XElement cell, string type)
10721091
{
10731092
// Force inlineStr for strings

‎tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGiteeIssuesTests.cs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void TestIssueI4DQUN()
150150
var sheetXml = SheetHelper.GetZipFileContent(path.ToString(), "xl/worksheets/sheet1.xml");
151151
// Template now uses inlineStr format (<is><t>...</t></is>) instead of SharedStrings (<v>...</v>)
152152
Assert.Contains("<t>Hello &amp; World &lt; , &gt; , \" , '</t>", sheetXml);
153-
Assert.Contains("<t>Hello &amp; Value &lt; , &gt; , \" , '</t>", sheetXml);
153+
Assert.Contains("<t xml:space=\"preserve\">Hello &amp; Value &lt; , &gt; , \" , '</t>", sheetXml);
154154
}
155155

156156
[Fact]

‎tests/MiniExcel.OpenXml.Tests/Issues/MiniExcelGithubIssuesTests.cs‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text.RegularExpressions;
2+
using System.Xml.Linq;
23
using MiniExcelLib.Core.Enums;
34
using MiniExcelLib.Core.Exceptions;
45
using MiniExcelLib.Core.Helpers;
@@ -1537,6 +1538,68 @@ public void TestIssue294()
15371538
}
15381539
}
15391540

1541+
/// Prefix and suffix blank space are hidden by Excel after FillTemplate - https://github.com/mini-software/MiniExcel/issues/295
1542+
[Fact]
1543+
public void TestIssue295()
1544+
{
1545+
static XElement GetInlineText(string sheetXml, string cellReference)
1546+
{
1547+
XNamespace ns = "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
1548+
var cell = XDocument.Parse(sheetXml)
1549+
.Descendants(ns + "c")
1550+
.Single(c => c.Attribute("r")?.Value == cellReference);
1551+
1552+
return cell.Element(ns + "is")!.Element(ns + "t")!;
1553+
}
1554+
1555+
{
1556+
using var path = AutoDeletingPath.Create();
1557+
var templatePath = PathHelper.GetFile("xlsx/TestTemplateEasyFill.xlsx");
1558+
var value = new
1559+
{
1560+
Name = " Jack",
1561+
CreateDate = new DateTime(2021, 01, 01),
1562+
VIP = true,
1563+
Points = 123
1564+
};
1565+
_excelTemplater.FillTemplate(path.ToString(), templatePath, value);
1566+
1567+
var sheetXml = SheetHelper.GetZipFileContent(path.ToString(), "xl/worksheets/sheet1.xml");
1568+
var a2 = GetInlineText(sheetXml, "A2");
1569+
Assert.Equal(" Jack", a2.Value);
1570+
Assert.Equal("preserve", a2.Attribute(XNamespace.Xml + "space")?.Value);
1571+
1572+
var e2 = GetInlineText(sheetXml, "E2");
1573+
Assert.Equal(" Jack has 123 points", e2.Value);
1574+
Assert.Equal("preserve", e2.Attribute(XNamespace.Xml + "space")?.Value);
1575+
}
1576+
{
1577+
using var path = AutoDeletingPath.Create();
1578+
var templatePath = PathHelper.GetFile("xlsx/TestTemplateBasicIEmumerableFill.xlsx");
1579+
var value = new
1580+
{
1581+
employees = new[]
1582+
{
1583+
new { name = " Jack", department = "HR " },
1584+
new { name = "Lisa", department = " IT " }
1585+
}
1586+
};
1587+
_excelTemplater.FillTemplate(path.ToString(), templatePath, value);
1588+
1589+
var sheetXml = SheetHelper.GetZipFileContent(path.ToString(), "xl/worksheets/sheet1.xml");
1590+
foreach (var (cellReference, expected) in new[] { ("A2", " Jack"), ("B2", "HR "), ("A3", "Lisa"), ("B3", " IT ") })
1591+
{
1592+
var t = GetInlineText(sheetXml, cellReference);
1593+
Assert.Equal(expected, t.Value);
1594+
Assert.Equal("preserve", t.Attribute(XNamespace.Xml + "space")?.Value);
1595+
}
1596+
1597+
var rows = _excelImporter.Query(path.ToString()).ToList();
1598+
Assert.Equal(" Jack", rows[1].A);
1599+
Assert.Equal(" IT ", rows[2].B);
1600+
}
1601+
}
1602+
15401603
// SaveAs support Image type - https://github.com/mini-software/MiniExcel/issues/304
15411604
[Fact]
15421605
public void TestIssue304()

0 commit comments

Comments
 (0)