[GH-ISSUE #524] Embedded image's vm attribute uses the shared image ref id, so mixing image kinds writes an out-of-range metadata index #520

Open
opened 2026-10-02 23:11:48 -06:00 by gitea-mirror · 3 comments
Owner

Originally created by @billdenney on GitHub (Jul 28, 2026).
Original GitHub issue: https://github.com/jmcnamara/libxlsxwriter/issues/524

Originally assigned to: @jmcnamara on GitHub.

An embedded image's cell carries a vm attribute indexing the
<valueMetadata> blocks, of which there is one per embedded image. But
_prepare_drawings() passes the shared image reference id, which also counts
floating, header and background images. In a workbook containing only embedded
images the two coincide and the file is correct; add any other image and vm
points past the end of the metadata. Excel reports "Repaired Records: Cell
information from /xl/worksheets/sheetN.xml" and the image is lost.

Cause

_prepare_drawings() (workbook.c:1137):

image_ref_id++;
ref_id = image_ref_id;          /* shared across every image kind */
self->num_embedded_images++;
...
worksheet_set_error_cell(worksheet, object_props, ref_id);

and worksheet_set_error_cell() (worksheet.c:11762) writes that straight into
the cell:

lxw_cell *cell = _new_error_cell(row_num, col_num, ref_id, object_props->format);

num_embedded_images is the count vm should be based on; image_ref_id is
not.

Observed

One embedded image, nothing else — correct:

<c r="C2" t="e" vm="1"><v>#VALUE!</v></c>
<valueMetadata count="1">...</valueMetadata>

The same embedded image in a workbook that also holds four other images:

<c r="D2" t="e" vm="4"><v>#VALUE!</v></c>
<valueMetadata count="1">...</valueMetadata>

vm="4" against a single metadata block.

The rich-value relationship is wrong in the same workbook for what looks like
the same reason — it targets the wrong picture:

<Relationship Id="rId1" Type=".../image" Target="../media/image1.png"/>

image1.png is 120x40 (a floating image); the embedded image is image4.png,
2x2.

Checked structurally across arrangements — vm values against
valueMetadata count:

Workbook vm count
1 embedded, nothing else 1 1 consistent
2 embedded, one sheet 1,2 2 consistent
2 embedded, two sheets 1,2 2 consistent
same image embedded twice 1,1 1 consistent (dedup correct)
1 floating + 1 embedded 2 1 out of range

Reproducing

#include "xlsxwriter.h"

int main(void) {
    lxw_workbook  *wb = workbook_new("embed_mix.xlsx");
    lxw_worksheet *s1 = workbook_add_worksheet(wb, NULL);
    lxw_worksheet *s2 = workbook_add_worksheet(wb, NULL);

    worksheet_insert_image(s1, 0, 0, "logo.png");   /* any non-embedded image */
    worksheet_embed_image(s2, 0, 0, "square.png");

    return workbook_close(wb);
}

Sheet 2's cell gets vm="2" while xl/metadata.xml has
<valueMetadata count="1">.

Suggested fix

Pass the embedded-image index — self->num_embedded_images after the
increment — to worksheet_set_error_cell() rather than ref_id, and record it
alongside the id in embedded_image_md5s so a duplicate embedded image resolves
to the original's rich-value index rather than its image ref id.

Found while adding image support to writexl (R). Version 1.2.4.

Originally created by @billdenney on GitHub (Jul 28, 2026). Original GitHub issue: https://github.com/jmcnamara/libxlsxwriter/issues/524 Originally assigned to: @jmcnamara on GitHub. An embedded image's cell carries a `vm` attribute indexing the `<valueMetadata>` blocks, of which there is one per embedded image. But `_prepare_drawings()` passes the *shared* image reference id, which also counts floating, header and background images. In a workbook containing only embedded images the two coincide and the file is correct; add any other image and `vm` points past the end of the metadata. Excel reports "Repaired Records: Cell information from /xl/worksheets/sheetN.xml" and the image is lost. ## Cause `_prepare_drawings()` (`workbook.c:1137`): ```c image_ref_id++; ref_id = image_ref_id; /* shared across every image kind */ self->num_embedded_images++; ... worksheet_set_error_cell(worksheet, object_props, ref_id); ``` and `worksheet_set_error_cell()` (`worksheet.c:11762`) writes that straight into the cell: ```c lxw_cell *cell = _new_error_cell(row_num, col_num, ref_id, object_props->format); ``` `num_embedded_images` is the count `vm` should be based on; `image_ref_id` is not. ## Observed One embedded image, nothing else — correct: ```xml <c r="C2" t="e" vm="1"><v>#VALUE!</v></c> <valueMetadata count="1">...</valueMetadata> ``` The same embedded image in a workbook that also holds four other images: ```xml <c r="D2" t="e" vm="4"><v>#VALUE!</v></c> <valueMetadata count="1">...</valueMetadata> ``` `vm="4"` against a single metadata block. The rich-value relationship is wrong in the same workbook for what looks like the same reason — it targets the wrong picture: ```xml <Relationship Id="rId1" Type=".../image" Target="../media/image1.png"/> ``` `image1.png` is 120x40 (a floating image); the embedded image is `image4.png`, 2x2. Checked structurally across arrangements — `vm` values against `valueMetadata count`: | Workbook | vm | count | | |---|---|---|---| | 1 embedded, nothing else | 1 | 1 | consistent | | 2 embedded, one sheet | 1,2 | 2 | consistent | | 2 embedded, two sheets | 1,2 | 2 | consistent | | same image embedded twice | 1,1 | 1 | consistent (dedup correct) | | **1 floating + 1 embedded** | **2** | **1** | **out of range** | ## Reproducing ```c #include "xlsxwriter.h" int main(void) { lxw_workbook *wb = workbook_new("embed_mix.xlsx"); lxw_worksheet *s1 = workbook_add_worksheet(wb, NULL); lxw_worksheet *s2 = workbook_add_worksheet(wb, NULL); worksheet_insert_image(s1, 0, 0, "logo.png"); /* any non-embedded image */ worksheet_embed_image(s2, 0, 0, "square.png"); return workbook_close(wb); } ``` Sheet 2's cell gets `vm="2"` while `xl/metadata.xml` has `<valueMetadata count="1">`. ## Suggested fix Pass the embedded-image index — `self->num_embedded_images` after the increment — to `worksheet_set_error_cell()` rather than `ref_id`, and record it alongside the id in `embedded_image_md5s` so a duplicate embedded image resolves to the original's rich-value index rather than its image ref id. Found while adding image support to writexl (R). Version 1.2.4.
Author
Owner

@billdenney commented on GitHub (Jul 28, 2026):

BTW, sorry for all the noise here. I'm working to make writexl implement the entire surface of libxlsxwriter, so I'm reporting what I find along the way. Hopefully this is helpful. For some of these that seem to have definitive solutions, I'll try to provide minimally-invasive PRs, too.

<!-- gh-comment-id:5110256028 --> @billdenney commented on GitHub (Jul 28, 2026): BTW, sorry for all the noise here. I'm working to make `writexl` implement the entire surface of `libxlsxwriter`, so I'm reporting what I find along the way. Hopefully this is helpful. For some of these that seem to have definitive solutions, I'll try to provide minimally-invasive PRs, too.
Author
Owner

@jmcnamara commented on GitHub (Jul 29, 2026):

@billdenney I am going to delete the previous interaction. You are doing your best. I am doing my best. Let's leave it at that. Keep posting the bugs/issues. My responses will be slow.

<!-- gh-comment-id:5113235421 --> @jmcnamara commented on GitHub (Jul 29, 2026): @billdenney I am going to delete the previous interaction. You are doing your best. I am doing my best. Let's leave it at that. Keep posting the bugs/issues. My responses will be slow.
Author
Owner

@jmcnamara commented on GitHub (Jul 29, 2026):

Thanks. I'll look into it.

<!-- gh-comment-id:5113817336 --> @jmcnamara commented on GitHub (Jul 29, 2026): Thanks. I'll look into it.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
github-starred/libxlsxwriter#520
No description provided.