Support static pictures inside cells with XLSX roundtrip (#61)

This commit is contained in:
Harrison O'Connor-Hoover
2026-10-10 12:43:27 -04:00
parent c5bf34d5d7
commit 9dc3aaf9fa
39 changed files with 1659 additions and 86 deletions
+31
View File
@@ -0,0 +1,31 @@
# Pictures inside cells
Choose **Insert → Pictures → Place in Cell**, select a PNG or JPEG file, and insert it into the
active cell. The picture replaces that cell's contents while keeping its formatting. Undo
restores the previous contents.
The picture fits inside the cell without stretching. It follows row and column resizing,
sorting, filtering, insertion/deletion and GridCraft's internal copy/cut/paste. Clearing
contents removes it; clearing or pasting only formats keeps it. Save as XLSX to preserve the
embedded image and its alt text, or export to PDF for a rendered copy.
**Place over Cells** retains the existing floating-picture behavior.
The command API accepts the same placement:
```json
{"command":"insert.picture","params":{"path":"picture.png","placement":"cell","at":"B2","alt":"Product photo"}}
```
This initial feature handles static pictures. Pasting an image from the operating system's
clipboard, converting between floating and cell pictures, and the `IMAGE()` function remain
separate work. Formula references and arithmetic do not carry pictures: the calculation
engine uses a `#VALUE!` fallback, including when a picture is referenced from another cell.
Text-only exports also use that fallback. The picture cell still counts as occupied and
blocks a formula's spill range.
XLSX stores these pictures as rich values. Readers without support for that format may show
`#VALUE!` instead of the image. GridCraft reads embedded PNG/JPEG rich-value images; unsupported
rich content, oversized or damaged picture metadata, and broken image references produce import
warnings while the workbook opens with scalar fallbacks. It does not fetch linked
images from the internet.
+28
View File
@@ -0,0 +1,28 @@
# Implementation decisions
## Static cell pictures
Store immutable picture payloads in `Sheet::cell_pictures`, a sparse map keyed by `CellRef`,
shared through `Arc`. Ordinary cells keep their original size. Content replacement removes the
map entry; copy, fill, sort and structural edits explicitly transport it, and undo shares bytes.
Serialize the map as address/payload pairs so JSON does not require struct-valued object keys.
Keep the scalar `#VALUE!` fallback used by XLSX consumers that cannot interpret rich images.
This first implementation does not extend formula evaluation to rich values or implement
`IMAGE()`; references and text-only exports use the fallback.
Write picture value metadata and existing dynamic-array cell metadata into the same workbook
metadata part, with independent index tables. Resolve input parts through their relationships.
Support embedded PNG/JPEG files without fetching external resources. See
[cell pictures](cell-pictures.md) for the supported user workflow and current limits.
Picture metadata is optional during XLSX import: part-size limits, record limits and relationship
read errors produce warnings and retain scalar cell fallbacks. The generic package size limits
remain unchanged. Rebased on main before revision; ODS/XLSB import PRs are still unmerged, and
`Package::read_limited` uses the same implementation as the incoming XLSB PR #101 to avoid
duplicate helpers when that work lands.
The UI cache uses a 64 MiB texture budget with at most 4,096 sparse entries. Images used in the
current or preceding frame are protected from eviction. If the visible set exceeds the budget,
new images show the existing placeholder until capacity is available; header-based admission
avoids repeatedly decoding overflow images. More than 128 small visible pictures now keep stable
texture handles instead of cycling through the cache every frame.