From e1935c77c79eae429b4caa793bb6d18baee6415a Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Tue, 2 Dec 2025 14:54:57 +0100 Subject: [PATCH 1/7] fix: Add x: namespace prefix to sheetData elements for Excel compatibility Dynamically generated sheetData content was missing namespace prefixes, causing XML inconsistency when worksheet root uses 'x:' prefix. Symptoms: Excel opens the file with correct file size but displays an empty worksheet - data is silently ignored due to malformed XML. --- EPPlus/ExcelWorksheet.cs | 76 ++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 12b29eb11..581a8b202 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -1460,12 +1460,12 @@ private void LoadMergeCells(XmlReader xr) /// The writer private void UpdateMergedCells(StreamWriter sw) { - sw.Write(""); + sw.Write(""); foreach (string address in _mergedCells) { - sw.Write("", address); + sw.Write("", address); } - sw.Write(""); + sw.Write(""); } /// /// Reads a row from the XML reader @@ -3442,7 +3442,7 @@ private void SaveXml(Stream stream) else { CreateNode("d:cols"); - CreateNode("d:sheetData"); + //CreateNode("d:sheetData"); CreateNode("d:mergeCells"); CreateNode("d:hyperlinks"); CreateNode("d:rowBreaks"); @@ -3533,13 +3533,13 @@ private void UpdateColBreaks(StreamWriter sw) var col=cse.Value._value as ExcelColumn; if (col != null && col.PageBreak) { - breaks.AppendFormat("", cse.Column); + breaks.AppendFormat("", cse.Column); count++; } } if (count > 0) { - sw.Write(string.Format("{1}", count, breaks.ToString())); + sw.Write(string.Format("{1}", count, breaks.ToString())); } } @@ -3554,13 +3554,13 @@ private void UpdateRowBreaks(StreamWriter sw) var row=cse.Value._value as RowInternal; if (row != null && row.PageBreak) { - breaks.AppendFormat("", cse.Row); + breaks.AppendFormat("", cse.Row); count++; } } if (count>0) { - sw.Write(string.Format("{1}", count, breaks.ToString())); + sw.Write(string.Format("{1}", count, breaks.ToString())); } } /// @@ -3574,13 +3574,13 @@ private void UpdateColumnData(StreamWriter sw) { if (first) { - sw.Write(""); + sw.Write(""); first = false; } var col = cse.Value._value as ExcelColumn; ExcelStyleCollection cellXfs = _package.Workbook.Styles.CellXfs; - sw.Write(""); + sw.Write(""); } } /// @@ -3636,7 +3636,7 @@ private void UpdateRowCellData(StreamWriter sw) var ss = _package.Workbook._sharedStrings; var styles = _package.Workbook.Styles; var cache = new StringBuilder(); - cache.Append(""); + cache.Append(""); FixSharedFormulas(); //Fixes Issue #32 @@ -3668,21 +3668,21 @@ private void UpdateRowCellData(StreamWriter sw) { if (f.IsArray) { - cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); } else { - cache.AppendFormat("{4}{5}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.AppendFormat("{4}{5}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); } } else if (f.IsArray) { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); + cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); } else { - cache.AppendFormat("{3}", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v, true)); + cache.AppendFormat("{3}", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v, true)); } } else @@ -3690,25 +3690,25 @@ private void UpdateRowCellData(StreamWriter sw) // We can also have a single cell array formula if (f.IsArray) { - cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); } else { - cache.AppendFormat("", f.Address, styleID < 0 ? 0 : styleID, GetCellType(v, true)); - cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v)); + cache.AppendFormat("", f.Address, styleID < 0 ? 0 : styleID, GetCellType(v, true)); + cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v)); } } } else if (formula != null && formula.ToString() != "") { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v, true)); - cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(formula.ToString()), GetFormulaValue(v)); + cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v, true)); + cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(formula.ToString()), GetFormulaValue(v)); } else { if (v == null && styleID > 0) { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); + cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); } else if (v != null) { @@ -3725,8 +3725,8 @@ private void UpdateRowCellData(StreamWriter sw) if ((TypeCompat.IsPrimitive(v) || v is double || v is decimal || v is DateTime || v is TimeSpan)) { //string sv = GetValueForXml(v); - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v)); - cache.AppendFormat("{0}", GetFormulaValue(v)); + cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v)); + cache.AppendFormat("{0}", GetFormulaValue(v)); } else { @@ -3741,8 +3741,8 @@ private void UpdateRowCellData(StreamWriter sw) { ix = ss[vString].pos; } - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); - cache.AppendFormat("{0}", ix); + cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); + cache.AppendFormat("{0}", ix); } } } @@ -3767,8 +3767,8 @@ private void UpdateRowCellData(StreamWriter sw) } columnStyles = null; - if (row != -1) cache.Append(""); - cache.Append(""); + if (row != -1) cache.Append(""); + cache.Append(""); sw.Write(cache.ToString()); sw.Flush(); } @@ -3856,7 +3856,7 @@ private object GetFormulaValue(object v) //{ if (v != null && v.ToString()!="") { - return "" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + ""; //Fixes issue 15071 + return "" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + ""; //Fixes issue 15071 } else { @@ -3933,9 +3933,9 @@ private string GetValueForXml(object v) } private void WriteRow(StringBuilder cache, ExcelStyleCollection cellXfs, int prevRow, int row) { - if (prevRow != -1) cache.Append(""); + if (prevRow != -1) cache.Append(""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); - cache.AppendFormat(" cellXf } private void WriteRow(StreamWriter sw, ExcelStyleCollection cellXfs, int prevRow, int row) { - if (prevRow != -1) sw.Write(""); + if (prevRow != -1) sw.Write(""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); - sw.Write(""); + sw.Write(""); first = false; } @@ -4056,7 +4056,7 @@ private void UpdateHyperLinks(StreamWriter sw) if (uri is ExcelHyperLink && !string.IsNullOrEmpty((uri as ExcelHyperLink).ReferenceAddress)) { ExcelHyperLink hl = uri as ExcelHyperLink; - sw.Write("", + sw.Write("", Cells[cse.Row, cse.Column, cse.Row + hl.RowSpann, cse.Column + hl.ColSpann].Address, ExcelCellBase.GetFullAddress(SecurityElement.Escape(Name), SecurityElement.Escape(hl.ReferenceAddress)), string.IsNullOrEmpty(hl.Display) ? "" : " display=\"" + SecurityElement.Escape(hl.Display) + "\"", @@ -4084,13 +4084,13 @@ private void UpdateHyperLinks(StreamWriter sw) if (uri is ExcelHyperLink) { ExcelHyperLink hl = uri as ExcelHyperLink; - sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id, + sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id, string.IsNullOrEmpty(hl.Display) ? "" : " display=\"" + SecurityElement.Escape(hl.Display) + "\"", string.IsNullOrEmpty(hl.ToolTip) ? "" : " tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\""); } else { - sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id); + sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id); } id = relationship.Id; } @@ -4099,7 +4099,7 @@ private void UpdateHyperLinks(StreamWriter sw) } if (!first) { - sw.Write(""); + sw.Write(""); } } /// From 5a745df44b3efb3b9ff06abb89834497c8e88f7b Mon Sep 17 00:00:00 2001 From: Thomas Herrmann Date: Fri, 19 Dec 2025 16:34:53 +0100 Subject: [PATCH 2/7] fix: Dynamic XML namespace prefixes for consistent Excel files When saving, inconsistent namespace prefixes were used, resulting in invalid XML documents. The prefix is now dynamically determined from the document and used consistently across all elements. - Extract _nsPrefix from WorksheetXml. DocumentElement - Replace all hardcoded "x:" prefixes with _nsPrefix - Add using alias RegexMatch to resolve naming conflict - Fix $"" combined with AppendFormat/string.Format calls --- EPPlus/ExcelWorksheet.cs | 156 ++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 74 deletions(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 581a8b202..aaad5ad2f 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -30,31 +30,32 @@ * Jan Källman Total rewrite 2010-03-01 * Jan Källman License changed GPL-->LGPL 2011-12-27 *******************************************************************************/ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; -using System.Globalization; -using System.IO; -using System.Security; -using System.Text; -using System.Text.RegularExpressions; -using System.Xml; +using OfficeOpenXml.Compatibility; using OfficeOpenXml.ConditionalFormatting; using OfficeOpenXml.DataValidation; using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Drawing.Vml; +using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using OfficeOpenXml.Packaging.Ionic.Zip; +using OfficeOpenXml.Sparkline; using OfficeOpenXml.Style.XmlAccess; using OfficeOpenXml.Table; using OfficeOpenXml.Table.PivotTable; using OfficeOpenXml.Utils; - +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.IO; using System.Linq; -using OfficeOpenXml.Compatibility; -using OfficeOpenXml.Sparkline; +using System.Security; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using RegexMatch = System.Text.RegularExpressions.Match; namespace OfficeOpenXml { @@ -371,6 +372,7 @@ internal void Clear(ExcelAddressBase Destination) private XmlDocument _worksheetXml; internal ExcelWorksheetView _sheetView; internal ExcelHeaderFooter _headerFooter; + private string _nsPrefix = ""; #endregion #region ExcelWorksheet Constructor /// @@ -1018,7 +1020,7 @@ private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding char[] block; int pos; StringBuilder sb = new StringBuilder(); - Match startmMatch, endMatch; + RegexMatch startmMatch, endMatch; do { int size = stream.Length < BLOCKSIZE ? (int)stream.Length : BLOCKSIZE; @@ -1081,7 +1083,7 @@ private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding } private void GetBlockPos(string xml, string tag, ref int start, ref int end) { - Match startmMatch, endMatch; + RegexMatch startmMatch, endMatch; startmMatch = Regex.Match(xml.Substring(start), string.Format("(<[^>]*{0}[^>]*>)", tag)); //"<[a-zA-Z:]*" + tag + "[?]*>"); if (!startmMatch.Success) //Not found @@ -1460,12 +1462,14 @@ private void LoadMergeCells(XmlReader xr) /// The writer private void UpdateMergedCells(StreamWriter sw) { - sw.Write(""); + sw.Write($"<{_nsPrefix}mergeCells>"); foreach (string address in _mergedCells) { - sw.Write("", address); + sw.Write($"<{_nsPrefix}mergeCell ref=\"{address}\" />"); + } - sw.Write(""); + sw.Write($""); + } /// /// Reads a row from the XML reader @@ -3433,6 +3437,16 @@ private static string GetTotalFunction(ExcelTableColumn col, string FunctionNum) private void SaveXml(Stream stream) { + // Ermittle den Namespace-Prefix aus dem Dokument + var worksheetNode = _worksheetXml.DocumentElement; + if (worksheetNode != null && !string.IsNullOrEmpty(worksheetNode.Prefix)) + { + _nsPrefix = worksheetNode.Prefix + ":"; + } + else + { + _nsPrefix = ""; + } //Create the nodes if they do not exist. StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8, 65536); if (this is ExcelChartsheet) @@ -3533,13 +3547,13 @@ private void UpdateColBreaks(StreamWriter sw) var col=cse.Value._value as ExcelColumn; if (col != null && col.PageBreak) { - breaks.AppendFormat("", cse.Column); + breaks.Append($"<{_nsPrefix}brk id=\"{cse.Column}\" max=\"16383\" man=\"1\"/>"); count++; } } if (count > 0) { - sw.Write(string.Format("{1}", count, breaks.ToString())); + sw.Write($"<{_nsPrefix}colBreaks count=\"{count}\" manualBreakCount=\"{count}\">{breaks}"); } } @@ -3554,13 +3568,13 @@ private void UpdateRowBreaks(StreamWriter sw) var row=cse.Value._value as RowInternal; if (row != null && row.PageBreak) { - breaks.AppendFormat("", cse.Row); + breaks.Append($"<{_nsPrefix}brk id=\"{cse.Row}\" max=\"1048575\" man=\"1\"/>"); count++; } } if (count>0) { - sw.Write(string.Format("{1}", count, breaks.ToString())); + sw.Write($"<{_nsPrefix}rowBreaks count=\"{count}\" manualBreakCount=\"{count}\">{breaks}"); } } /// @@ -3574,13 +3588,13 @@ private void UpdateColumnData(StreamWriter sw) { if (first) { - sw.Write(""); + sw.Write($"<{_nsPrefix}cols>"); first = false; } var col = cse.Value._value as ExcelColumn; ExcelStyleCollection cellXfs = _package.Workbook.Styles.CellXfs; - sw.Write(""); + sw.Write($""); } } /// @@ -3636,7 +3650,7 @@ private void UpdateRowCellData(StreamWriter sw) var ss = _package.Workbook._sharedStrings; var styles = _package.Workbook.Styles; var cache = new StringBuilder(); - cache.Append(""); + cache.Append($"<{_nsPrefix}sheetData>"); FixSharedFormulas(); //Fixes Issue #32 @@ -3668,21 +3682,21 @@ private void UpdateRowCellData(StreamWriter sw) { if (f.IsArray) { - cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}><{_nsPrefix}f ref=\"{f.Address}\" t=\"array\">{ConvertUtil.ExcelEscapeString(f.Formula)}{GetFormulaValue(v)}"); } else { - cache.AppendFormat("{4}{5}", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}><{_nsPrefix}f ref=\"{f.Address}\" t=\"shared\" si=\"{sfId}\">{ConvertUtil.ExcelEscapeString(f.Formula)}{GetFormulaValue(v)}"); } } else if (f.IsArray) { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"/>"); } else { - cache.AppendFormat("{3}", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v, true)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}><{_nsPrefix}f t=\"shared\" si=\"{sfId}\"/>{GetFormulaValue(v)}"); } } else @@ -3690,25 +3704,25 @@ private void UpdateRowCellData(StreamWriter sw) // We can also have a single cell array formula if (f.IsArray) { - cache.AppendFormat("{3}{4}", cse.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}><{_nsPrefix}f ref=\"{f.Address}:{f.Address}\" t=\"array\">{ConvertUtil.ExcelEscapeString(f.Formula)}{GetFormulaValue(v)}"); } else { - cache.AppendFormat("", f.Address, styleID < 0 ? 0 : styleID, GetCellType(v, true)); - cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v)); + cache.Append($"<{_nsPrefix}c r=\"{f.Address}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}>"); + cache.Append($"<{_nsPrefix}f>{ConvertUtil.ExcelEscapeString(f.Formula)}{GetFormulaValue(v)}"); } } } else if (formula != null && formula.ToString() != "") { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v, true)); - cache.AppendFormat("{0}{1}", ConvertUtil.ExcelEscapeString(formula.ToString()), GetFormulaValue(v)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v, true)}>"); + cache.Append($"<{_nsPrefix}f>{ConvertUtil.ExcelEscapeString(formula.ToString())}{GetFormulaValue(v)}"); } else { if (v == null && styleID > 0) { - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"/>"); } else if (v != null) { @@ -3725,8 +3739,8 @@ private void UpdateRowCellData(StreamWriter sw) if ((TypeCompat.IsPrimitive(v) || v is double || v is decimal || v is DateTime || v is TimeSpan)) { //string sv = GetValueForXml(v); - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v)); - cache.AppendFormat("{0}", GetFormulaValue(v)); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\"{GetCellType(v)}>"); + cache.Append($"{GetFormulaValue(v)}"); } else { @@ -3741,8 +3755,8 @@ private void UpdateRowCellData(StreamWriter sw) { ix = ss[vString].pos; } - cache.AppendFormat("", cse.CellAddress, styleID < 0 ? 0 : styleID); - cache.AppendFormat("{0}", ix); + cache.Append($"<{_nsPrefix}c r=\"{cse.CellAddress}\" s=\"{(styleID < 0 ? 0 : styleID)}\" t=\"s\">"); + cache.Append($"<{_nsPrefix}v>{ix}"); } } } @@ -3767,8 +3781,8 @@ private void UpdateRowCellData(StreamWriter sw) } columnStyles = null; - if (row != -1) cache.Append(""); - cache.Append(""); + if (row != -1) cache.Append($""); + cache.Append($""); sw.Write(cache.ToString()); sw.Flush(); } @@ -3856,7 +3870,7 @@ private object GetFormulaValue(object v) //{ if (v != null && v.ToString()!="") { - return "" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + ""; //Fixes issue 15071 + return $"<{_nsPrefix}v>" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + $""; //Fixes issue 15071 } else { @@ -3933,9 +3947,9 @@ private string GetValueForXml(object v) } private void WriteRow(StringBuilder cache, ExcelStyleCollection cellXfs, int prevRow, int row) { - if (prevRow != -1) cache.Append(""); + if (prevRow != -1) cache.Append($""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); - cache.AppendFormat(" cellXf } if (currRow.Height >= 0) { - cache.AppendFormat(string.Format(CultureInfo.InvariantCulture, " ht=\"{0}\"", currRow.Height)); + cache.Append(string.Format(CultureInfo.InvariantCulture, " ht=\"{0}\"", currRow.Height)); if (currRow.CustomHeight) { cache.Append(" customHeight=\"1\""); @@ -3956,7 +3970,7 @@ private void WriteRow(StringBuilder cache, ExcelStyleCollection cellXf if (currRow.OutlineLevel > 0) { - cache.AppendFormat(" outlineLevel =\"{0}\"", currRow.OutlineLevel); + cache.Append($" outlineLevel=\"{currRow.OutlineLevel}\""); if (currRow.Collapsed) { if (currRow.Hidden) @@ -3977,15 +3991,15 @@ private void WriteRow(StringBuilder cache, ExcelStyleCollection cellXf var s = GetStyleInner(row, 0); if (s > 0) { - cache.AppendFormat(" s=\"{0}\" customFormat=\"1\"", cellXfs[s].newID); + cache.Append($" s=\"{cellXfs[s].newID}\" customFormat=\"1\""); } cache.Append(">"); } private void WriteRow(StreamWriter sw, ExcelStyleCollection cellXfs, int prevRow, int row) { - if (prevRow != -1) sw.Write(""); + if (prevRow != -1) sw.Write($""); //ulong rowID = ExcelRow.GetRowID(SheetID, row); - sw.Write(" cellXfs, i if (currRow.OutlineLevel > 0) { - sw.Write(" outlineLevel =\"{0}\"", currRow.OutlineLevel); + sw.Write($" outlineLevel=\"{currRow.OutlineLevel}\""); if (currRow.Collapsed) { if (currRow.Hidden) @@ -4027,7 +4041,7 @@ private void WriteRow(StreamWriter sw, ExcelStyleCollection cellXfs, i var s = GetStyleInner(row, 0); if (s > 0) { - sw.Write(" s=\"{0}\" customFormat=\"1\"", cellXfs[s].newID); + sw.Write($" s=\"{cellXfs[s].newID}\" customFormat=\"1\""); } sw.Write(">"); } @@ -4047,7 +4061,7 @@ private void UpdateHyperLinks(StreamWriter sw) var uri = _hyperLinks.GetValue(cse.Row, cse.Column); if (first && uri != null) { - sw.Write(""); + sw.Write($"<{_nsPrefix}hyperlinks>"); first = false; } @@ -4056,12 +4070,8 @@ private void UpdateHyperLinks(StreamWriter sw) if (uri is ExcelHyperLink && !string.IsNullOrEmpty((uri as ExcelHyperLink).ReferenceAddress)) { ExcelHyperLink hl = uri as ExcelHyperLink; - sw.Write("", - Cells[cse.Row, cse.Column, cse.Row + hl.RowSpann, cse.Column + hl.ColSpann].Address, - ExcelCellBase.GetFullAddress(SecurityElement.Escape(Name), SecurityElement.Escape(hl.ReferenceAddress)), - string.IsNullOrEmpty(hl.Display) ? "" : " display=\"" + SecurityElement.Escape(hl.Display) + "\"", - string.IsNullOrEmpty(hl.ToolTip) ? "" : " tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\""); - } + sw.Write($"<{_nsPrefix}hyperlink ref=\"{Cells[cse.Row, cse.Column, cse.Row + hl.RowSpann, cse.Column + hl.ColSpann].Address}\" location=\"{ExcelCellBase.GetFullAddress(SecurityElement.Escape(Name), SecurityElement.Escape(hl.ReferenceAddress))}\"{(string.IsNullOrEmpty(hl.Display) ? "" : $" display=\"{SecurityElement.Escape(hl.Display)}\"")}{(string.IsNullOrEmpty(hl.ToolTip) ? "" : $" tooltip=\"{SecurityElement.Escape(hl.ToolTip)}\"")}/>"); + } else if( uri!=null) { string id; @@ -4081,17 +4091,15 @@ private void UpdateHyperLinks(StreamWriter sw) else { var relationship = Part.CreateRelationship(hyp, Packaging.TargetMode.External, ExcelPackage.schemaHyperlink); - if (uri is ExcelHyperLink) - { - ExcelHyperLink hl = uri as ExcelHyperLink; - sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id, - string.IsNullOrEmpty(hl.Display) ? "" : " display=\"" + SecurityElement.Escape(hl.Display) + "\"", - string.IsNullOrEmpty(hl.ToolTip) ? "" : " tooltip=\"" + SecurityElement.Escape(hl.ToolTip) + "\""); - } - else - { - sw.Write("", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id); - } + if (uri is ExcelHyperLink) + { + ExcelHyperLink hl = uri as ExcelHyperLink; + sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\"{(string.IsNullOrEmpty(hl.Display) ? "" : $" display=\"{SecurityElement.Escape(hl.Display)}\"")}{(string.IsNullOrEmpty(hl.ToolTip) ? "" : $" tooltip=\"{SecurityElement.Escape(hl.ToolTip)}\"")}" + $" r:id=\"{relationship.Id}\"/>"); + } + else + { + sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\" r:id=\"{relationship.Id}\"/>"); + } id = relationship.Id; } //cell.HyperLinkRId = id; @@ -4099,7 +4107,7 @@ private void UpdateHyperLinks(StreamWriter sw) } if (!first) { - sw.Write(""); + sw.Write($""); } } /// From 76e971c2c6a132797a5dc8cf2b77f8e7508aefad Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 5 May 2026 22:07:57 -0400 Subject: [PATCH 3/7] Refactor GetWorksheetNamespacePrefix Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- EPPlus/ExcelWorksheet.cs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index aaad5ad2f..1319c9082 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -3435,18 +3435,26 @@ private static string GetTotalFunction(ExcelTableColumn col, string FunctionNum) return string.Format("SUBTOTAL({0},{1}[{2}])", FunctionNum, col._tbl.Name, escapedColumn); } - private void SaveXml(Stream stream) + private string GetWorksheetNamespacePrefix() { - // Ermittle den Namespace-Prefix aus dem Dokument var worksheetNode = _worksheetXml.DocumentElement; - if (worksheetNode != null && !string.IsNullOrEmpty(worksheetNode.Prefix)) + if (worksheetNode == null) { - _nsPrefix = worksheetNode.Prefix + ":"; + return string.Empty; } - else + + var prefix = worksheetNode.Prefix; + if (string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(worksheetNode.NamespaceURI) == false) { - _nsPrefix = ""; + prefix = worksheetNode.GetPrefixOfNamespace(worksheetNode.NamespaceURI); } + + return string.IsNullOrEmpty(prefix) ? string.Empty : prefix + ":"; + } + + private void SaveXml(Stream stream) + { + _nsPrefix = GetWorksheetNamespacePrefix(); //Create the nodes if they do not exist. StreamWriter sw = new StreamWriter(stream, System.Text.Encoding.UTF8, 65536); if (this is ExcelChartsheet) From ca0c04269ea8b403e3b8287012c73af988a351dc Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 5 May 2026 22:09:11 -0400 Subject: [PATCH 4/7] Update EPPlus/ExcelWorksheet.cs --- EPPlus/ExcelWorksheet.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 1319c9082..a5321072c 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -36,7 +36,6 @@ using OfficeOpenXml.Drawing; using OfficeOpenXml.Drawing.Chart; using OfficeOpenXml.Drawing.Vml; -using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup; using OfficeOpenXml.FormulaParsing.LexicalAnalysis; using OfficeOpenXml.Packaging.Ionic.Zip; using OfficeOpenXml.Sparkline; From b857d6ad83e5f63d8cd6bcf6036c46fe33c0a056 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 5 May 2026 22:09:32 -0400 Subject: [PATCH 5/7] Update EPPlus/ExcelWorksheet.cs --- EPPlus/ExcelWorksheet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index a5321072c..06a68b34d 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -3463,7 +3463,7 @@ private void SaveXml(Stream stream) else { CreateNode("d:cols"); - //CreateNode("d:sheetData"); + CreateNode("d:sheetData"); CreateNode("d:mergeCells"); CreateNode("d:hyperlinks"); CreateNode("d:rowBreaks"); From c222138980898a610d34d78b7f99353da926f8c8 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 5 May 2026 22:10:13 -0400 Subject: [PATCH 6/7] Apply suggestion from @Shane32 --- EPPlus/ExcelWorksheet.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 06a68b34d..46294139a 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -4098,15 +4098,15 @@ private void UpdateHyperLinks(StreamWriter sw) else { var relationship = Part.CreateRelationship(hyp, Packaging.TargetMode.External, ExcelPackage.schemaHyperlink); - if (uri is ExcelHyperLink) - { - ExcelHyperLink hl = uri as ExcelHyperLink; - sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\"{(string.IsNullOrEmpty(hl.Display) ? "" : $" display=\"{SecurityElement.Escape(hl.Display)}\"")}{(string.IsNullOrEmpty(hl.ToolTip) ? "" : $" tooltip=\"{SecurityElement.Escape(hl.ToolTip)}\"")}" + $" r:id=\"{relationship.Id}\"/>"); - } - else - { - sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\" r:id=\"{relationship.Id}\"/>"); - } + if (uri is ExcelHyperLink) + { + ExcelHyperLink hl = uri as ExcelHyperLink; + sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\"{(string.IsNullOrEmpty(hl.Display) ? "" : $" display=\"{SecurityElement.Escape(hl.Display)}\"")}{(string.IsNullOrEmpty(hl.ToolTip) ? "" : $" tooltip=\"{SecurityElement.Escape(hl.ToolTip)}\"")}" + $" r:id=\"{relationship.Id}\"/>"); + } + else + { + sw.Write($"<{_nsPrefix}hyperlink ref=\"{ExcelCellBase.GetAddress(cse.Row, cse.Column)}\" r:id=\"{relationship.Id}\"/>"); + } id = relationship.Id; } //cell.HyperLinkRId = id; From b4ac302b67c5098d7b5df82bbd6cf78df9ba5ee5 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Thu, 7 May 2026 13:55:50 -0400 Subject: [PATCH 7/7] Update empty sheetData node construction --- EPPlus/ExcelWorksheet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EPPlus/ExcelWorksheet.cs b/EPPlus/ExcelWorksheet.cs index 46294139a..5b56192e5 100644 --- a/EPPlus/ExcelWorksheet.cs +++ b/EPPlus/ExcelWorksheet.cs @@ -1069,7 +1069,7 @@ private string GetWorkSheetXml(Stream stream, long start, long end, out Encoding } } endMatch = Regex.Match(s, string.Format("(]*{0}[^>]*>)", "sheetData")); - xml += "" + s.Substring(endMatch.Index + endMatch.Length, s.Length - (endMatch.Index + endMatch.Length)); + xml += startmMatch.Value.Substring(0, startmMatch.Value.Length - 1) + "/>" + s.Substring(endMatch.Index + endMatch.Length, s.Length - (endMatch.Index + endMatch.Length)); } if (sr.Peek() > -1) {