Skip to content

设置图片嵌入到单元格 - #994

Draft
2399087410 wants to merge 5 commits into
mini-software:masterfrom
2399087410:master
Draft

设置图片嵌入到单元格#994
2399087410 wants to merge 5 commits into
mini-software:masterfrom
2399087410:master

Conversation

@2399087410

@2399087410 2399087410 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
        var configuration = new OpenXmlConfiguration
        {
            FastMode = true,
            EnableConvertByteArray = true,
            EmbedImagesInCell = true,
        };

Summary by CodeRabbit

  • New Features

    • Added support for embedding images directly inside Excel 365 cells.
    • Added configuration to enable cell-embedded images during export.
    • Supports multiple images, mixed embedded and floating images, and common image formats.
  • Documentation

    • Added English and Chinese guidance, examples, Microsoft 365 requirements, and compatibility limitations.
  • Bug Fixes

    • Preserved existing floating-image behavior when cell embedding is disabled.

2399087410 and others added 5 commits August 3, 2026 11:06
Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds XlsxImgType.PlaceInCell and EmbedImagesInCell. The OpenXML writer routes byte-array images to rich-data serialization for Excel 365. Tests cover generated package parts, mixed image modes, configuration behavior, and documentation updates.

Changes

In-cell image embedding

Layer / File(s) Summary
PlaceInCell contracts and configuration
src/MiniExcel.Core/Enums/XlsxImgType.cs, src/MiniExcel.OpenXml/OpenXmlConfiguration.cs, src/MiniExcel.OpenXml/Picture/OpenXmlPicture.cs
Adds the PlaceInCell image type and EmbedImagesInCell option. Documents Excel 365 requirements and ignored sizing and location settings.
Writer routing and image partitioning
src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs, src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs, src/MiniExcel.OpenXml/Writer/*
Validates fast mode, partitions in-cell and drawing images, writes in-cell image records, and omits drawing references when embedding is enabled.
Rich-data package serialization
src/MiniExcel.OpenXml/Picture/OpenXmlPlaceInCellImplement.cs
Creates media, relationships, content types, rich-value metadata, worksheet cells, and expanded dimensions for in-cell images.
Validation and documentation
tests/MiniExcel.OpenXml.Tests/MiniExcelPlaceInCellTests.cs, README.md, README.zh-CN.md, README_V2.md, tests/MiniExcel.Tests.Common/Localization.Designer.cs
Tests memory and file output, multiple images, mixed anchors, and configuration behavior. Documents usage and compatibility.
Estimated code review effort: 4 (Complex) ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OpenXmlWriter
  participant OpenXmlPictureImplement
  participant OpenXmlPlaceInCellImplement
  participant ExcelPackage
  OpenXmlWriter->>OpenXmlPictureImplement: collect and partition image records
  OpenXmlPictureImplement->>OpenXmlPlaceInCellImplement: add PlaceInCell images
  OpenXmlPlaceInCellImplement->>ExcelPackage: write media and rich-data XML
  ExcelPackage-->>OpenXmlWriter: updated worksheet package
Loading

Possibly related PRs

Suggested reviewers: michelebastione

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题明确描述了将图片嵌入 Excel 单元格这一主要变更。
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (6)
src/MiniExcel.OpenXml/Picture/OpenXmlPlaceInCellImplement.cs (1)

507-573: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Consider updating the row spans attribute.

UpsertImageCell can add a cell to an existing row at a column outside the row's current spans range. The code does not update spans. Excel treats spans as a hint and recomputes it, so files still open. Some stricter OOXML consumers and validators report an inconsistency.

Either widen spans when you insert a cell, or remove the attribute from rows you modify.

🤖 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/Picture/OpenXmlPlaceInCellImplement.cs` around lines
507 - 573, Update UpsertImageCell and its row insertion flow so a modified row’s
spans attribute remains consistent with the inserted cell’s column; either widen
the existing spans range to include the new column or remove spans from the
affected row. Preserve the current cell creation and update behavior.
src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs (1)

25-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused OpenXmlReader instance.

Line 31 creates a reader, but the code only calls the static method OpenXmlReader.GetWorkbookRelsAsync with excelArchive.EntryCollection. The instance is never used. It also introduces ambiguous ownership of excelArchive, because both the reader and disposableExcelArchive can dispose the same archive.

If the reader instance is not required, drop it and keep only the static call.

♻️ Proposed cleanup
             var excelArchive = await OpenXmlZip.CreateAsync(excelStream, leaveOpen: true, cancellationToken: cancellationToken)
                 .ConfigureAwait(false);
             await using var disposableExcelArchive = excelArchive.ConfigureAwait(false);
-            using var reader = OpenXmlReader.CreateFromArchive(excelArchive, null);
             var rels = await OpenXmlReader.GetWorkbookRelsAsync(excelArchive.EntryCollection, cancellationToken).ConfigureAwait(false);
             sheetEntries = rels?.ToList() ?? [];
🤖 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/Picture/OpenXmlPictureImplement.cs` around lines 25 -
34, Remove the unused OpenXmlReader instance created in the sheet-list
initialization block, leaving OpenXmlReader.GetWorkbookRelsAsync as the only
reader-related operation and retaining disposableExcelArchive as the archive
owner.
src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs (1)

90-111: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider reusing AddFilesToZipAsync with a filter.

This loop duplicates the entry-writing logic of AddFilesToZipAsync. The only difference is the !f.IsImage predicate. Two copies of the same write path can drift, for example if compression level or cancellation handling changes.

Add an optional predicate parameter to AddFilesToZipAsync and call it from both branches.

🤖 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/Writer/OpenXmlWriter.cs` around lines 90 - 111, Update
AddFilesToZipAsync to accept an optional file predicate and apply it while
selecting _files, preserving the existing behavior when no predicate is
provided. Replace the inline non-image entry-writing loop in the
EmbedImagesInCell branch of OpenXmlWriter with AddFilesToZipAsync using
!f.IsImage, while keeping the shared-string, workbook, content-type, and image
generation calls unchanged.
tests/MiniExcel.Tests.Common/Localization.Designer.cs (1)

3-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Verify this locale change is intentional; it is unrelated to the PlaceInCell feature.

This file only translates auto-generated XML-doc comments into Chinese. This file has GeneratedCodeAttribute marking it as produced by StronglyTypedResourceBuilder from a .resx file. Comments here usually change only when someone regenerates the designer file using a tool running under a different UI culture. This diff has no connection to the PlaceInCell embedding feature in this PR.

Confirm this is an intended, tracked localization effort. If it is an accidental side effect of the local toolchain, revert it to keep the file in English, consistent with the rest of the codebase, and avoid repeated unrelated diffs on future regenerations.

Also applies to: 16-21, 36-36, 50-51, 64-64, 73-73, 82-82, 91-91

🤖 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 `@tests/MiniExcel.Tests.Common/Localization.Designer.cs` around lines 3 - 7,
Revert the unrelated Chinese localization changes in the generated
Localization.Designer.cs comments, restoring the English tool-generated text
used elsewhere in the codebase; leave the generated resource code and
PlaceInCell changes untouched.
tests/MiniExcel.OpenXml.Tests/MiniExcelPlaceInCellTests.cs (1)

195-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a negative-path test for EmbedImagesInCell prerequisites.

Both tests here set FastMode = true and EnableConvertByteArray = true. OpenXmlConfiguration.EmbedImagesInCell is documented as requiring EnableConvertByteArray = true and FastMode = true. No test exercises EmbedImagesInCell = true combined with FastMode = false or EnableConvertByteArray = false. Add a test asserting the documented behavior (exception or fallback) when these prerequisites are not met, so the contract stays enforced as the writer implementation evolves.

🤖 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 `@tests/MiniExcel.OpenXml.Tests/MiniExcelPlaceInCellTests.cs` around lines 195
- 249, Add a negative-path test near
EmbedImagesInCell_ExportConfig_WritesPlaceInCellNotDrawing that sets
EmbedImagesInCell = true while disabling FastMode or EnableConvertByteArray.
Assert the documented exception or fallback behavior, using the existing
_excelExporter.Export flow and OpenXmlConfiguration setup, and cover both
missing-prerequisite combinations if the contract distinguishes them.
README.md (1)

1503-1514: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the EmbedImagesInCell configuration option in all README variants.

Each README documents only the manual AddPicture(ImgType = XlsxImgType.PlaceInCell) path. None of them documents OpenXmlConfiguration.EmbedImagesInCell, which is the config-driven approach highlighted in the PR objectives (FastMode = true, EnableConvertByteArray = true, EmbedImagesInCell = true) and covered by the new EmbedImagesInCell_ExportConfig_WritesPlaceInCellNotDrawing and EmbedImagesInCell_False_KeepsFloatingDrawing tests. Users exporting byte[] columns will not discover this feature without reading source code.

  • README.md#L1503-L1514: add a short example showing OpenXmlConfiguration { FastMode = true, EnableConvertByteArray = true, EmbedImagesInCell = true } used with an exporter, and state its prerequisites.
  • README.zh-CN.md#L1669-L1680: add the same example and prerequisites in Chinese, matching the surrounding translation style.
  • README_V2.md#L1793-L1807: add the same example using the MiniExcel.Exporters.GetOpenXmlExporter() fluent style already used in this file.
🤖 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 `@README.md` around lines 1503 - 1514, Document the config-driven
EmbedImagesInCell option in README.md lines 1503-1514 with an
OpenXmlConfiguration example using FastMode, EnableConvertByteArray, and
EmbedImagesInCell, plus its prerequisites; add the equivalent Chinese example
and prerequisites in README.zh-CN.md lines 1669-1680, matching local translation
style; and add the same configuration through the existing
MiniExcel.Exporters.GetOpenXmlExporter() fluent style in README_V2.md lines
1793-1807. Mention that this supports embedding exported byte[] images in cells
and requires the applicable OpenXML/Excel prerequisites.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@src/MiniExcel.OpenXml/Picture/OpenXmlPlaceInCellImplement.cs`:
- Around line 217-231: In EnsureWorkbookRichDataRelationships, load or create
the XML document with LoadOrCreateXml before calling GetOrCreateEntry for
WorkbookRelsPath. Then resolve the entry and save the populated document as
before, preserving the existing relationship updates and root validation.
- Around line 619-630: Update the no-change guard in ExpandDimension to also
verify that the existing start coordinates already cover the incoming maxCol and
maxRow; allow execution to continue when the image extends before the range
start, so the start-adjustment logic updates the dimension ref while preserving
the current early return for truly unchanged ranges.

In `@src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs`:
- Around line 51-54: Extend the configuration validation near the existing
EmbedImagesInCell/FastMode guard in OpenXmlWriter to also reject
EmbedImagesInCell when EnableConvertByteArray is false. Keep the existing
FastMode validation and use an InvalidOperationException with a clear
requirement message for the byte-array conversion setting.

---

Nitpick comments:
In `@README.md`:
- Around line 1503-1514: Document the config-driven EmbedImagesInCell option in
README.md lines 1503-1514 with an OpenXmlConfiguration example using FastMode,
EnableConvertByteArray, and EmbedImagesInCell, plus its prerequisites; add the
equivalent Chinese example and prerequisites in README.zh-CN.md lines 1669-1680,
matching local translation style; and add the same configuration through the
existing MiniExcel.Exporters.GetOpenXmlExporter() fluent style in README_V2.md
lines 1793-1807. Mention that this supports embedding exported byte[] images in
cells and requires the applicable OpenXML/Excel prerequisites.

In `@src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs`:
- Around line 25-34: Remove the unused OpenXmlReader instance created in the
sheet-list initialization block, leaving OpenXmlReader.GetWorkbookRelsAsync as
the only reader-related operation and retaining disposableExcelArchive as the
archive owner.

In `@src/MiniExcel.OpenXml/Picture/OpenXmlPlaceInCellImplement.cs`:
- Around line 507-573: Update UpsertImageCell and its row insertion flow so a
modified row’s spans attribute remains consistent with the inserted cell’s
column; either widen the existing spans range to include the new column or
remove spans from the affected row. Preserve the current cell creation and
update behavior.

In `@src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs`:
- Around line 90-111: Update AddFilesToZipAsync to accept an optional file
predicate and apply it while selecting _files, preserving the existing behavior
when no predicate is provided. Replace the inline non-image entry-writing loop
in the EmbedImagesInCell branch of OpenXmlWriter with AddFilesToZipAsync using
!f.IsImage, while keeping the shared-string, workbook, content-type, and image
generation calls unchanged.

In `@tests/MiniExcel.OpenXml.Tests/MiniExcelPlaceInCellTests.cs`:
- Around line 195-249: Add a negative-path test near
EmbedImagesInCell_ExportConfig_WritesPlaceInCellNotDrawing that sets
EmbedImagesInCell = true while disabling FastMode or EnableConvertByteArray.
Assert the documented exception or fallback behavior, using the existing
_excelExporter.Export flow and OpenXmlConfiguration setup, and cover both
missing-prerequisite combinations if the contract distinguishes them.

In `@tests/MiniExcel.Tests.Common/Localization.Designer.cs`:
- Around line 3-7: Revert the unrelated Chinese localization changes in the
generated Localization.Designer.cs comments, restoring the English
tool-generated text used elsewhere in the codebase; leave the generated resource
code and PlaceInCell changes untouched.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c24d6bff-a2a9-4cb5-81cb-a6536aa6b41c

📥 Commits

Reviewing files that changed from the base of the PR and between 5195330 and e2f3554.

📒 Files selected for processing (13)
  • README.md
  • README.zh-CN.md
  • README_V2.md
  • src/MiniExcel.Core/Enums/XlsxImgType.cs
  • src/MiniExcel.OpenXml/OpenXmlConfiguration.cs
  • src/MiniExcel.OpenXml/Picture/OpenXmlPicture.cs
  • src/MiniExcel.OpenXml/Picture/OpenXmlPictureImplement.cs
  • src/MiniExcel.OpenXml/Picture/OpenXmlPlaceInCellImplement.cs
  • src/MiniExcel.OpenXml/Reader/OpenXmlReader.cs
  • src/MiniExcel.OpenXml/Writer/OpenXmlWriter.XmlGeneration.cs
  • src/MiniExcel.OpenXml/Writer/OpenXmlWriter.cs
  • tests/MiniExcel.OpenXml.Tests/MiniExcelPlaceInCellTests.cs
  • tests/MiniExcel.Tests.Common/Localization.Designer.cs

Comment on lines +217 to +231
private static void EnsureWorkbookRichDataRelationships(ZipArchive archive)
{
var entry = GetOrCreateEntry(archive, WorkbookRelsPath);
var doc = LoadOrCreateXml(archive, WorkbookRelsPath, CreateRelationshipsXml);
var root = doc.DocumentElement
?? throw new InvalidOperationException("workbook.xml.rels is missing a root element.");

EnsureRelationship(root, RelSheetMetadata, "metadata.xml");
EnsureRelationship(root, RelRdRichValue, "richData/rdrichvalue.xml");
EnsureRelationship(root, RelRdRichValueStructure, "richData/rdrichvaluestructure.xml");
EnsureRelationship(root, RelRdRichValueTypes, "richData/rdRichValueTypes.xml");
EnsureRelationship(root, RelRichValueRel, "richData/richValueRel.xml");

SaveXml(doc, entry);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Reorder GetOrCreateEntry and LoadOrCreateXml.

Line 219 can create an empty xl/_rels/workbook.xml.rels entry. Line 220 then calls LoadOrCreateXml for the same path. Because the entry now exists, LoadOrCreateXml no longer uses the CreateRelationshipsXml factory. It falls through to LoadXml, which returns a bare new XmlDocument() for an empty stream (line 758-759). That document has no root element, so line 221 throws "workbook.xml.rels is missing a root element.".

A valid xlsx always contains this part, so the path is not reachable today. The ordering still defeats the factory fallback and makes the helper fragile.

Load the document first, then resolve the entry.

🛠️ Proposed fix
     private static void EnsureWorkbookRichDataRelationships(ZipArchive archive)
     {
-        var entry = GetOrCreateEntry(archive, WorkbookRelsPath);
         var doc = LoadOrCreateXml(archive, WorkbookRelsPath, CreateRelationshipsXml);
+        var entry = GetOrCreateEntry(archive, WorkbookRelsPath);
         var root = doc.DocumentElement
                    ?? throw new InvalidOperationException("workbook.xml.rels is missing a root element.");
📝 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
private static void EnsureWorkbookRichDataRelationships(ZipArchive archive)
{
var entry = GetOrCreateEntry(archive, WorkbookRelsPath);
var doc = LoadOrCreateXml(archive, WorkbookRelsPath, CreateRelationshipsXml);
var root = doc.DocumentElement
?? throw new InvalidOperationException("workbook.xml.rels is missing a root element.");
EnsureRelationship(root, RelSheetMetadata, "metadata.xml");
EnsureRelationship(root, RelRdRichValue, "richData/rdrichvalue.xml");
EnsureRelationship(root, RelRdRichValueStructure, "richData/rdrichvaluestructure.xml");
EnsureRelationship(root, RelRdRichValueTypes, "richData/rdRichValueTypes.xml");
EnsureRelationship(root, RelRichValueRel, "richData/richValueRel.xml");
SaveXml(doc, entry);
}
private static void EnsureWorkbookRichDataRelationships(ZipArchive archive)
{
var doc = LoadOrCreateXml(archive, WorkbookRelsPath, CreateRelationshipsXml);
var entry = GetOrCreateEntry(archive, WorkbookRelsPath);
var root = doc.DocumentElement
?? throw new InvalidOperationException("workbook.xml.rels is missing a root element.");
EnsureRelationship(root, RelSheetMetadata, "metadata.xml");
EnsureRelationship(root, RelRdRichValue, "richData/rdrichvalue.xml");
EnsureRelationship(root, RelRdRichValueStructure, "richData/rdrichvaluestructure.xml");
EnsureRelationship(root, RelRdRichValueTypes, "richData/rdRichValueTypes.xml");
EnsureRelationship(root, RelRichValueRel, "richData/richValueRel.xml");
SaveXml(doc, entry);
}
🤖 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/Picture/OpenXmlPlaceInCellImplement.cs` around lines
217 - 231, In EnsureWorkbookRichDataRelationships, load or create the XML
document with LoadOrCreateXml before calling GetOrCreateEntry for
WorkbookRelsPath. Then resolve the entry and save the populated document as
before, preserving the existing relationship updates and root validation.

Comment on lines +619 to +630
var newEndCol = Math.Max(endCol, maxCol);
var newEndRow = Math.Max(endRow, maxRow);
if (newEndCol == endCol && newEndRow == endRow)
return;

var newStart = CellReferenceConverter.GetCellFromCoordinates(Math.Min(startCol, maxCol), Math.Min(startRow, maxRow));
// Keep original start if image is within/after the existing range start
if (startCol <= maxCol && startRow <= maxRow)
newStart = start;

var newEnd = CellReferenceConverter.GetCellFromCoordinates(newEndCol, newEndRow);
dimension.SetAttribute("ref", newStart == newEnd ? newStart : $"{newStart}:{newEnd}");

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 | 🟡 Minor | ⚡ Quick win

ExpandDimension never expands the range start.

Line 621 returns early when the end of the range does not grow. The start-adjustment logic at lines 624-627 is therefore unreachable whenever maxCol <= endCol and maxRow <= endRow.

Example: the existing dimension/@ref is C3:D4 and an image is placed at A1. newEndCol == endCol and newEndRow == endRow, so the method returns and the ref stays C3:D4. The ref no longer covers the populated cell A1.

This matters for round-tripping. OpenXmlReader.TryGetMaxRowColumnIndexAsync stops parsing as soon as it reads the dimension element and derives max row and column from it. A ref that omits written cells produces short reads.

Include the start in the no-change guard.

🐛 Proposed fix
         var newEndCol = Math.Max(endCol, maxCol);
         var newEndRow = Math.Max(endRow, maxRow);
-        if (newEndCol == endCol && newEndRow == endRow)
+        var newStartCol = Math.Min(startCol, maxCol);
+        var newStartRow = Math.Min(startRow, maxRow);
+        if (newEndCol == endCol && newEndRow == endRow
+            && newStartCol == startCol && newStartRow == startRow)
             return;
 
-        var newStart = CellReferenceConverter.GetCellFromCoordinates(Math.Min(startCol, maxCol), Math.Min(startRow, maxRow));
-        // Keep original start if image is within/after the existing range start
-        if (startCol <= maxCol && startRow <= maxRow)
-            newStart = start;
-
+        var newStart = newStartCol == startCol && newStartRow == startRow
+            ? start
+            : CellReferenceConverter.GetCellFromCoordinates(newStartCol, newStartRow);
         var newEnd = CellReferenceConverter.GetCellFromCoordinates(newEndCol, newEndRow);
         dimension.SetAttribute("ref", newStart == newEnd ? newStart : $"{newStart}:{newEnd}");
📝 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
var newEndCol = Math.Max(endCol, maxCol);
var newEndRow = Math.Max(endRow, maxRow);
if (newEndCol == endCol && newEndRow == endRow)
return;
var newStart = CellReferenceConverter.GetCellFromCoordinates(Math.Min(startCol, maxCol), Math.Min(startRow, maxRow));
// Keep original start if image is within/after the existing range start
if (startCol <= maxCol && startRow <= maxRow)
newStart = start;
var newEnd = CellReferenceConverter.GetCellFromCoordinates(newEndCol, newEndRow);
dimension.SetAttribute("ref", newStart == newEnd ? newStart : $"{newStart}:{newEnd}");
var newEndCol = Math.Max(endCol, maxCol);
var newEndRow = Math.Max(endRow, maxRow);
var newStartCol = Math.Min(startCol, maxCol);
var newStartRow = Math.Min(startRow, maxRow);
if (newEndCol == endCol && newEndRow == endRow
&& newStartCol == startCol && newStartRow == startRow)
return;
var newStart = newStartCol == startCol && newStartRow == startRow
? start
: CellReferenceConverter.GetCellFromCoordinates(newStartCol, newStartRow);
var newEnd = CellReferenceConverter.GetCellFromCoordinates(newEndCol, newEndRow);
dimension.SetAttribute("ref", newStart == newEnd ? newStart : $"{newStart}:{newEnd}");
🤖 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/Picture/OpenXmlPlaceInCellImplement.cs` around lines
619 - 630, Update the no-change guard in ExpandDimension to also verify that the
existing start coordinates already cover the incoming maxCol and maxRow; allow
execution to continue when the image extends before the range start, so the
start-adjustment logic updates the dimension ref while preserving the current
early return for truly unchanged ranges.

Comment on lines +51 to +54
// Place-in-cell rewriting needs ZipArchiveMode.Update (seekable entry rewrite).
if (conf is { EmbedImagesInCell: true, FastMode: false })
throw new InvalidOperationException("EmbedImagesInCell requires FastMode to be enabled");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Also validate EnableConvertByteArray for EmbedImagesInCell.

The XML doc on OpenXmlConfiguration.EmbedImagesInCell states that the option requires EnableConvertByteArray = true. This check enforces only FastMode.

If a caller sets EmbedImagesInCell = true and EnableConvertByteArray = false, the byte[] branch in GetCellValue (OpenXmlWriter.XmlGeneration.cs line 226) is skipped. Execution falls through to the final value.ToString() return, which writes the literal text System.Byte[] into the cell. The feature fails silently and produces incorrect output.

Add the matching guard so the misconfiguration fails fast.

🛡️ Proposed fix
         // Place-in-cell rewriting needs ZipArchiveMode.Update (seekable entry rewrite).
         if (conf is { EmbedImagesInCell: true, FastMode: false })
             throw new InvalidOperationException("EmbedImagesInCell requires FastMode to be enabled");
+
+        if (conf is { EmbedImagesInCell: true, EnableConvertByteArray: false })
+            throw new InvalidOperationException("EmbedImagesInCell requires EnableConvertByteArray to be enabled");
📝 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
// Place-in-cell rewriting needs ZipArchiveMode.Update (seekable entry rewrite).
if (conf is { EmbedImagesInCell: true, FastMode: false })
throw new InvalidOperationException("EmbedImagesInCell requires FastMode to be enabled");
// Place-in-cell rewriting needs ZipArchiveMode.Update (seekable entry rewrite).
if (conf is { EmbedImagesInCell: true, FastMode: false })
throw new InvalidOperationException("EmbedImagesInCell requires FastMode to be enabled");
if (conf is { EmbedImagesInCell: true, EnableConvertByteArray: false })
throw new InvalidOperationException("EmbedImagesInCell requires EnableConvertByteArray to be enabled");
🤖 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/Writer/OpenXmlWriter.cs` around lines 51 - 54, Extend
the configuration validation near the existing EmbedImagesInCell/FastMode guard
in OpenXmlWriter to also reject EmbedImagesInCell when EnableConvertByteArray is
false. Keep the existing FastMode validation and use an
InvalidOperationException with a clear requirement message for the byte-array
conversion setting.

@2399087410 2399087410 changed the title 设置单元格嵌入到单元格 设置图片嵌入到单元格 Aug 3, 2026
@michelebastione michelebastione self-assigned this Aug 3, 2026
@michelebastione
michelebastione marked this pull request as draft August 3, 2026 17:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants