Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 38 additions & 38 deletions EPPlus/ExcelWorksheet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1460,12 +1460,12 @@ private void LoadMergeCells(XmlReader xr)
/// <param name="sw">The writer</param>
private void UpdateMergedCells(StreamWriter sw)
{
sw.Write("<mergeCells>");
sw.Write("<x:mergeCells>");
foreach (string address in _mergedCells)
{
sw.Write("<mergeCell ref=\"{0}\" />", address);
sw.Write("<x:mergeCell ref=\"{0}\" />", address);
}
sw.Write("</mergeCells>");
sw.Write("</x:mergeCells>");
Comment on lines +1463 to +1468
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for main spreadsheet elements. The 'x:' prefix is registered for schemaMicrosoftExcel (urn:schemas-microsoft-com:office:excel) which is used for VML drawings, not for main spreadsheet elements like mergeCells, mergeCell, etc.

Main spreadsheet elements should either be:

  1. Unprefixed (in the default namespace), OR
  2. Match the prefix used in the original worksheet XML document

Hardcoding 'x:' prefix will break standard Excel files where these elements are unprefixed. Consider dynamically detecting and using the appropriate prefix from the worksheet root element instead of hardcoding 'x:'.

Copilot uses AI. Check for mistakes.
}
/// <summary>
/// Reads a row from the XML reader
Expand Down Expand Up @@ -3442,7 +3442,7 @@ private void SaveXml(Stream stream)
else
{
CreateNode("d:cols");
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting out CreateNode("d:sheetData") while adding 'x:' prefixes to dynamically generated sheetData content creates an inconsistency. If this line is commented out because sheetData will be dynamically generated, there should be an explanation comment. Additionally, this change coupled with the hardcoded 'x:' prefixes suggests a fundamental issue with namespace handling that needs a more comprehensive solution rather than piecemeal changes.

Suggested change
CreateNode("d:cols");
CreateNode("d:cols");
// The sheetData node is generated dynamically below, so we do not create it here.
// WARNING: Ensure that dynamically generated sheetData content uses consistent XML namespaces.

Copilot uses AI. Check for mistakes.
CreateNode("d:sheetData");
//CreateNode("d:sheetData");
CreateNode("d:mergeCells");
CreateNode("d:hyperlinks");
CreateNode("d:rowBreaks");
Expand Down Expand Up @@ -3533,13 +3533,13 @@ private void UpdateColBreaks(StreamWriter sw)
var col=cse.Value._value as ExcelColumn;
if (col != null && col.PageBreak)
{
breaks.AppendFormat("<brk id=\"{0}\" max=\"16383\" man=\"1\"/>", cse.Column);
breaks.AppendFormat("<x:brk id=\"{0}\" max=\"16383\" man=\"1\"/>", cse.Column);
count++;
}
}
if (count > 0)
{
sw.Write(string.Format("<colBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</colBreaks>", count, breaks.ToString()));
sw.Write(string.Format("<x:colBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</x:colBreaks>", count, breaks.ToString()));
Comment on lines +3536 to +3542
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for colBreaks and brk elements. These are main spreadsheet schema elements that should either be unprefixed (default namespace) or match the prefix used in the original worksheet XML. The 'x:' prefix is reserved for VML drawing elements (schemaMicrosoftExcel), not for these elements. This will cause XML validation errors and break compatibility with standard Excel files.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant call to 'ToString' on a String object.

Copilot uses AI. Check for mistakes.
}
}

Expand All @@ -3554,13 +3554,13 @@ private void UpdateRowBreaks(StreamWriter sw)
var row=cse.Value._value as RowInternal;
if (row != null && row.PageBreak)
{
breaks.AppendFormat("<brk id=\"{0}\" max=\"1048575\" man=\"1\"/>", cse.Row);
breaks.AppendFormat("<x:brk id=\"{0}\" max=\"1048575\" man=\"1\"/>", cse.Row);
count++;
}
}
if (count>0)
{
sw.Write(string.Format("<rowBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</rowBreaks>", count, breaks.ToString()));
sw.Write(string.Format("<x:rowBreaks count=\"{0}\" manualBreakCount=\"{0}\">{1}</x:rowBreaks>", count, breaks.ToString()));
Comment on lines +3557 to +3563
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for rowBreaks and brk elements. These are main spreadsheet schema elements that should either be unprefixed (default namespace) or match the prefix used in the original worksheet XML. The 'x:' prefix is reserved for VML drawing elements (schemaMicrosoftExcel), not for these elements. This will cause XML validation errors and break compatibility with standard Excel files.

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant call to 'ToString' on a String object.

Copilot uses AI. Check for mistakes.
}
}
/// <summary>
Expand All @@ -3574,13 +3574,13 @@ private void UpdateColumnData(StreamWriter sw)
{
if (first)
{
sw.Write("<cols>");
sw.Write("<x:cols>");
first = false;
}
var col = cse.Value._value as ExcelColumn;
ExcelStyleCollection<ExcelXfs> cellXfs = _package.Workbook.Styles.CellXfs;

sw.Write("<col min=\"{0}\" max=\"{1}\"", col.ColumnMin, col.ColumnMax);
sw.Write("<x:col min=\"{0}\" max=\"{1}\"", col.ColumnMin, col.ColumnMax);
if (col.Hidden == true)
{
sw.Write(" hidden=\"1\"");
Expand Down Expand Up @@ -3620,7 +3620,7 @@ private void UpdateColumnData(StreamWriter sw)
}
if (!first)
{
sw.Write("</cols>");
sw.Write("</x:cols>");
}
}
/// <summary>
Expand All @@ -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("<sheetData>");
cache.Append("<x:sheetData>");


FixSharedFormulas(); //Fixes Issue #32
Expand Down Expand Up @@ -3668,47 +3668,47 @@ private void UpdateRowCellData(StreamWriter sw)
{
if (f.IsArray)
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{5}><f ref=\"{2}\" t=\"array\">{3}</f>{4}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{5}><x:f ref=\"{2}\" t=\"array\">{3}</x:f>{4}</x:c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true));
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opening <f> tag is missing the x: namespace prefix while the closing tag has it (</x:f>). This creates an XML mismatch. Change <f ref= to <x:f ref= for consistency.

Copilot uses AI. Check for mistakes.
}
else
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{6}><f ref=\"{2}\" t=\"shared\" si=\"{3}\">{4}</f>{5}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{6}><x:f ref=\"{2}\" t=\"shared\" si=\"{3}\">{4}</x:f>{5}</x:c>", cse.CellAddress, styleID < 0 ? 0 : styleID, f.Address, sfId, ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true));
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opening <f> tag is missing the x: namespace prefix while the closing tag has it (</x:f>). This creates an XML mismatch. Change <f ref= to <x:f ref= for consistency.

Copilot uses AI. Check for mistakes.
}

}
else if (f.IsArray)
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"/>", cse.CellAddress, styleID < 0 ? 0 : styleID);
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"/>", cse.CellAddress, styleID < 0 ? 0 : styleID);
}
else
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{4}><f t=\"shared\" si=\"{2}\"/>{3}</c>", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v, true));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{4}><x:f t=\"shared\" si=\"{2}\"/>{3}</x:c>", cse.CellAddress, styleID < 0 ? 0 : styleID, sfId, GetFormulaValue(v), GetCellType(v, true));
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opening <f> tag is missing the x: namespace prefix. Change <f t= to <x:f t= for consistency with other formula elements.

Copilot uses AI. Check for mistakes.
}
}
else
{
// We can also have a single cell array formula
if (f.IsArray)
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{5}><f ref=\"{2}\" t=\"array\">{3}</f>{4}</c>", 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("<x:c r=\"{0}\" s=\"{1}\"{5}><x:f ref=\"{2}\" t=\"array\">{3}</x:f>{4}</x:c>", cse.CellAddress, styleID < 0 ? 0 : styleID, string.Format("{0}:{1}", f.Address, f.Address), ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v), GetCellType(v, true));
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The opening <f> tag is missing the x: namespace prefix while the closing tag has it (</x:f>). This creates an XML mismatch. Change <f ref= to <x:f ref= for consistency.

Copilot uses AI. Check for mistakes.
}
else
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{2}>", f.Address, styleID < 0 ? 0 : styleID, GetCellType(v, true));
cache.AppendFormat("<f>{0}</f>{1}</c>", ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{2}>", f.Address, styleID < 0 ? 0 : styleID, GetCellType(v, true));
cache.AppendFormat("<x:f>{0}</x:f>{1}</x:c>", ConvertUtil.ExcelEscapeString(f.Formula), GetFormulaValue(v));
}
}
}
else if (formula != null && formula.ToString() != "")
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"{2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v, true));
cache.AppendFormat("<f>{0}</f>{1}</c>", ConvertUtil.ExcelEscapeString(formula.ToString()), GetFormulaValue(v));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v, true));
cache.AppendFormat("<x:f>{0}</x:f>{1}</x:c>", ConvertUtil.ExcelEscapeString(formula.ToString()), GetFormulaValue(v));
}
else
{
if (v == null && styleID > 0)
{
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\"/>", cse.CellAddress, styleID < 0 ? 0 : styleID);
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"/>", cse.CellAddress, styleID < 0 ? 0 : styleID);
}
else if (v != null)
{
Expand All @@ -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("<c r=\"{0}\" s=\"{1}\"{2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v));
cache.AppendFormat("{0}</c>", GetFormulaValue(v));
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\"{2}>", cse.CellAddress, styleID < 0 ? 0 : styleID, GetCellType(v));
cache.AppendFormat("{0}</x:c>", GetFormulaValue(v));
}
else
{
Expand All @@ -3741,8 +3741,8 @@ private void UpdateRowCellData(StreamWriter sw)
{
ix = ss[vString].pos;
}
cache.AppendFormat("<c r=\"{0}\" s=\"{1}\" t=\"s\">", cse.CellAddress, styleID < 0 ? 0 : styleID);
cache.AppendFormat("<v>{0}</v></c>", ix);
cache.AppendFormat("<x:c r=\"{0}\" s=\"{1}\" t=\"s\">", cse.CellAddress, styleID < 0 ? 0 : styleID);
cache.AppendFormat("<x:v>{0}</x:v></x:c>", ix);
}
}
}
Expand All @@ -3767,8 +3767,8 @@ private void UpdateRowCellData(StreamWriter sw)
}
columnStyles = null;

if (row != -1) cache.Append("</row>");
cache.Append("</sheetData>");
if (row != -1) cache.Append("</x:row>");
cache.Append("</x:sheetData>");
sw.Write(cache.ToString());
sw.Flush();
}
Expand Down Expand Up @@ -3856,7 +3856,7 @@ private object GetFormulaValue(object v)
//{
if (v != null && v.ToString()!="")
{
return "<v>" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + "</v>"; //Fixes issue 15071
return "<x:v>" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + "</x:v>"; //Fixes issue 15071
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for the 'v' (value) element. This is a main spreadsheet schema element that should be unprefixed or match the worksheet's namespace prefix. Using 'x:' (which is for VML elements) will cause XML validation errors.

Suggested change
return "<x:v>" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + "</x:v>"; //Fixes issue 15071
return "<v>" + ConvertUtil.ExcelEscapeString(GetValueForXml(v)) + "</v>"; //Fixes issue 15071

Copilot uses AI. Check for mistakes.
}
else
{
Expand Down Expand Up @@ -3933,9 +3933,9 @@ private string GetValueForXml(object v)
}
private void WriteRow(StringBuilder cache, ExcelStyleCollection<ExcelXfs> cellXfs, int prevRow, int row)
{
if (prevRow != -1) cache.Append("</row>");
if (prevRow != -1) cache.Append("</x:row>");
//ulong rowID = ExcelRow.GetRowID(SheetID, row);
cache.AppendFormat("<row r=\"{0}\"", row);
cache.AppendFormat("<x:row r=\"{0}\"", row);
Comment on lines +3936 to +3938
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for 'row' elements. Row elements are part of the main spreadsheet schema and should either be unprefixed (default namespace) or match the prefix used in the original worksheet XML. The 'x:' prefix is reserved for VML drawing elements. This will break standard Excel files.

Copilot uses AI. Check for mistakes.
RowInternal currRow = GetValueInner(row, 0) as RowInternal;
if (currRow != null)
{
Expand Down Expand Up @@ -3983,9 +3983,9 @@ private void WriteRow(StringBuilder cache, ExcelStyleCollection<ExcelXfs> cellXf
}
private void WriteRow(StreamWriter sw, ExcelStyleCollection<ExcelXfs> cellXfs, int prevRow, int row)
{
if (prevRow != -1) sw.Write("</row>");
if (prevRow != -1) sw.Write("</x:row>");
//ulong rowID = ExcelRow.GetRowID(SheetID, row);
sw.Write("<row r=\"{0}\"", row);
sw.Write("<x:row r=\"{0}\"", row);
Comment on lines +3986 to +3988
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'x:' namespace prefix is incorrect for 'row' elements in the StreamWriter version. Row elements are part of the main spreadsheet schema and should either be unprefixed (default namespace) or match the prefix used in the original worksheet XML. The 'x:' prefix is reserved for VML drawing elements. This will break standard Excel files.

Copilot uses AI. Check for mistakes.
RowInternal currRow = GetValueInner(row, 0) as RowInternal;
if (currRow!=null)
{
Expand Down Expand Up @@ -4047,7 +4047,7 @@ private void UpdateHyperLinks(StreamWriter sw)
var uri = _hyperLinks.GetValue(cse.Row, cse.Column);
if (first && uri != null)
{
sw.Write("<hyperlinks>");
sw.Write("<x:hyperlinks>");
first = false;
}

Expand All @@ -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("<hyperlink ref=\"{0}\" location=\"{1}\"{2}{3}/>",
sw.Write("<x:hyperlink ref=\"{0}\" location=\"{1}\"{2}{3}/>",
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) + "\"",
Expand Down Expand Up @@ -4084,13 +4084,13 @@ private void UpdateHyperLinks(StreamWriter sw)
if (uri is ExcelHyperLink)
{
ExcelHyperLink hl = uri as ExcelHyperLink;
sw.Write("<hyperlink ref=\"{0}\"{2}{3} r:id=\"{1}\"/>", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id,
sw.Write("<x:hyperlink ref=\"{0}\"{2}{3} r:id=\"{1}\"/>", 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("<hyperlink ref=\"{0}\" r:id=\"{1}\"/>", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id);
sw.Write("<x:hyperlink ref=\"{0}\" r:id=\"{1}\"/>", ExcelCellBase.GetAddress(cse.Row, cse.Column), relationship.Id);
}
id = relationship.Id;
}
Expand All @@ -4099,7 +4099,7 @@ private void UpdateHyperLinks(StreamWriter sw)
}
if (!first)
{
sw.Write("</hyperlinks>");
sw.Write("</x:hyperlinks>");
}
}
/// <summary>
Expand Down