Skip to content
Open
Show file tree
Hide file tree
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
13 changes: 11 additions & 2 deletions src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -936,8 +936,17 @@ private void ProcessFormulas(StringBuilder rowXml, int rowIndex)
fNode.SetValue(str.Value[2..]);
str.AddBeforeSelf(fNode);
str.Remove();

var celRef = CellReferenceConverter.GetCellFromCoordinates(index, rowIndex);
// the cell no longer holds an inline string; keeping t="inlineStr" without an <is>
// child is schema-invalid and Excel rejects the package
cell.Attribute("t")?.Remove();

// take the column from the cell's own reference — the running index is wrong for
// sparse rows (cells without content are not emitted, so position != column)
var cellRef = cell.Attribute("r")?.Value;
var columnLetters = cellRef is null ? "" : new string(cellRef.TakeWhile(char.IsLetter).ToArray());
var celRef = string.IsNullOrEmpty(columnLetters)
? CellReferenceConverter.GetCellFromCoordinates(index, rowIndex)
: $"{columnLetters}{rowIndex}";
Comment on lines +943 to +949

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the complete cell address for calcChain entries.

rowIndex can differ from the rendered cell’s row in enumerable/merged rows, so this produces a chain entry for the wrong cell. Use c@r directly when present.

Proposed fix
 var cellRef = cell.Attribute("r")?.Value;
-var columnLetters = cellRef is null ? "" : new string(cellRef.TakeWhile(char.IsLetter).ToArray());
-var celRef = string.IsNullOrEmpty(columnLetters)
+var celRef = string.IsNullOrEmpty(cellRef)
     ? CellReferenceConverter.GetCellFromCoordinates(index, rowIndex)
-    : $"{columnLetters}{rowIndex}";
+    : cellRef;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// take the column from the cell's own reference — the running index is wrong for
// sparse rows (cells without content are not emitted, so position != column)
var cellRef = cell.Attribute("r")?.Value;
var columnLetters = cellRef is null ? "" : new string(cellRef.TakeWhile(char.IsLetter).ToArray());
var celRef = string.IsNullOrEmpty(columnLetters)
? CellReferenceConverter.GetCellFromCoordinates(index, rowIndex)
: $"{columnLetters}{rowIndex}";
// take the column from the cell's own reference — the running index is wrong for
// sparse rows (cells without content are not emitted, so position != column)
var cellRef = cell.Attribute("r")?.Value;
var celRef = string.IsNullOrEmpty(cellRef)
? CellReferenceConverter.GetCellFromCoordinates(index, rowIndex)
: cellRef;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.Impl.cs` around lines 943 -
949, Update the cell reference assignment near cellRef so calcChain entries use
the complete c@r value directly whenever it is present, rather than
reconstructing it with rowIndex or the running index. Preserve the existing
coordinate fallback only when c@r is missing.

_calcChainCellRefs.Add(celRef);
}
}
Expand Down
34 changes: 8 additions & 26 deletions src/MiniExcel.OpenXml/Templates/OpenXmlTemplate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,41 +158,23 @@ await originalEntryStream.CopyToAsync(newEntryStream
// batch add sheet
await BatchAddSheetsToWorkbookAsync(outputFileArchive.ZipFile, originalArchive, allSheetInfos, cancellationToken).ConfigureAwait(false);

// create mode we need to not create first then create here
var calcChain = outputFileArchive.EntryCollection.FirstOrDefault(e
// The template's own calcChain cannot be reused: row insertion shifts formula cells and its
// entries would point at the old addresses. It is regenerated from the rendered formulas —
// and when none were rendered, dropped entirely, because a calcChain with no <c> entries is
// schema-invalid and Excel rejects the whole package. Excel rebuilds the chain on open, so
// dropping it is always safe. (The stale-template copy that used to run when no chain entry
// was created here is intentionally gone for the same reason.)
var calcChain = outputFileArchive.EntryCollection.FirstOrDefault(e
=> e.FullName.TrimStart('/').Equals(ExcelFileNames.CalcChain, StringComparison.OrdinalIgnoreCase));

if (calcChain is not null)
if (calcChain is not null && _calcChainContent.Length > 0)
{
var calcChainEntry = outputFileArchive.ZipFile.CreateEntry(calcChain.FullName);
var calcChainStream = await calcChainEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableChainEntryStream = calcChainStream.ConfigureAwait(false);

await CalcChainHelper.GenerateCalcChainSheetAsync(calcChainStream, _calcChainContent.ToString(), cancellationToken).ConfigureAwait(false);
}
else
{
foreach (var entry in originalArchive.Entries)
{
if (entry.FullName.TrimStart('/').Equals(ExcelFileNames.CalcChain, StringComparison.OrdinalIgnoreCase))
{
var newEntry = outputFileArchive.ZipFile.CreateEntry(entry.FullName);

// Copy the content of the original entry to the new entry
var originalEntryStream = await entry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableEntryStream = originalEntryStream.ConfigureAwait(false);

var newEntryStream = await newEntry.OpenAsync(cancellationToken).ConfigureAwait(false);
await using var disposableNewEntryStream = newEntryStream.ConfigureAwait(false);

await originalEntryStream.CopyToAsync(newEntryStream
#if NET
, cancellationToken
#endif
).ConfigureAwait(false);
}
}
}

#if NET10_0_OR_GREATER
await outputFileArchive.ZipFile.DisposeAsync().ConfigureAwait(false);
Expand Down
Loading