Skip to content

fix(html): name a linked ooxml image relative to the document - #727

Merged
andiwand merged 1 commit into
mainfrom
fix/ooxml-linked-image-paths
Aug 21, 2026
Merged

fix(html): name a linked ooxml image relative to the document#727
andiwand merged 1 commit into
mainfrom
fix/ooxml-linked-image-paths

Conversation

@andiwand

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Closes opendocument-app/OpenDocument.droid#551.

Every image in a .docx / .xlsx renders as the broken-image placeholder when the host links images rather than embedding them. ODF is fine, which is what made it easy to miss.

Cause

The engines disagree on what an image href is. odf_document.cpp:935 returns the raw xlink:href, which is relative — Pictures/x.png. The ooxml ones resolve the relationship into an absolute container path — ooxml_text_document.cpp:573 joins onto AbsPath("/word"), ooxml_spreadsheet_document.cpp:416 onto the part's directory — and translate_image emitted whichever it got.

An absolute location cannot work. resource_at matches it against the request path by exact string, and the http route strips the leading /, so /word/media/image1.jpeg served under /file/<prefix>/ misses either way. bring_offline is worse than a 404: it does Path(output).join(RelPath(*location)) and RelPath throws on an absolute string, so translate --embed-images=false on a docx fails before writing anything.

Neither knob the downstream issue proposed helps. relative_resource_paths gates its rebase on resource.is_shipped(), and HttpServer::prefix_pattern is ([a-zA-Z0-9_-]+), so there is no root to mount at.

Fix

Make the location relative where the resource is built, once, for every engine:

const std::string path = Path(image.href()).make_relative().string();

image_file() and image_is_internal() already make_absolute() the href, so the engines stay as they are and Image::href() keeps its meaning.

Tests

html.linked_images_are_served renders an odt, a docx and an xlsx with embed_images = false and asserts each image location is relative, exists on the service, and serves bytes. It fails on both ooxml paths without the change.

The reference output renders with embed_images = true, so it never exercised the linked form — that is the gap this closes. Its docx / xlsx / odt output is byte-identical after the change, since an embedded image carries no location.

Not in scope

Two neighbours found while confirming this, each worth its own issue:

  • ooxml_text_parser.cpp:226 maps every a:graphicData to ElementType::image, so a chart, SmartArt or embedded OLE becomes an image with an empty href — the src="" seen in a handful of reference documents. file-sample_100kB.docx is one: a picture and a chart, and the chart is the empty one.
  • document_element.cpp:508 creates a non-internal image resource with is_external = false.

The odf engines return an image href as it is written in the file, which
is relative; the ooxml ones resolve the relationship into an absolute
container path. The html layer emitted either verbatim, so an absolute
one resolved against the server root rather than against the document -
every image in a docx or xlsx 404ed for a host serving the service under
a mount point, and `bring_offline` threw `not a relative path` outright.

Make the location relative where the resource is built, so it holds for
every engine. The reference output embeds its images and never rendered
the linked form, hence the new test.

Closes opendocument-app/OpenDocument.droid#551

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuzekTz46XJdLDrLgt4hnJ
@andiwand
andiwand force-pushed the fix/ooxml-linked-image-paths branch from 7910ce2 to c24e4cb Compare August 21, 2026 08:34
@andiwand
andiwand enabled auto-merge (squash) August 21, 2026 08:35
@andiwand
andiwand merged commit ba64cb0 into main Aug 21, 2026
36 checks passed
@andiwand
andiwand deleted the fix/ooxml-linked-image-paths branch August 21, 2026 08:51
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.

Images in OOXML documents render as "Error: image not found or unsupported"

1 participant