fix(super-editor): persist data-URI images set as SDT preset content (SD-3116)#3516
Conversation
Register data URI image sources as DOCX media parts during export so the relationship target resolves correctly. Map the svg+xml MIME subtype to a .svg extension when deriving the media filename.
β¦processing Detect SVG data URIs with known finite sizes and register them in place during browser-path image handling, skipping the canvas-based resize pipeline that strips vector content. Normalize svg+xml extensions to .svg when generating media filenames so the relationship target matches the stored media key.
β¦tadata Only emit the empty-inline-SDT placeholder when the resolved metadata describes a structuredContent node, so other inline SDT variants aren't collapsed into a placeholder text run when their content is empty.
Parse data URIs by inspecting the meta header rather than assuming base64 encoding. URL-encoded payloads (e.g. SVG with charset=utf-8) are now decoded as text and written through as-is, while base64 payloads continue through atob/binary conversion. Adds coverage for the non-base64 SVG path in handleBase64 and the browser registration plugin.
Replace the base64-only data URL regex with an allowlist-based validator that accepts URL-encoded SVG payloads while still restricting raster image MIME types to base64. Applies to both inline image runs and field annotation images, and adds tests for the SVG, raster, and non-image cases.
Wrap the imported helper in a local function so the public API remains a stable indirection rather than a direct re-export.
|
I'll skip the spec lookup since permission wasn't granted β but I can review based on the actual XML structure being emitted. Looking at the three changed files: Status: PASS The changes are primarily data-handling refactors, not OOXML structural changes:
One observation worth flagging (not a spec violation, but a Word interop note): plain SVG data URIs are now exported as primary |
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 881cda9726
βΉοΈ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
cubic analysis
No issues found across 27 files
Linked issue analysis
Linked issue: SD-3116: Bug: image set as presetContent in SDT field does not persist on reload
| Status | Acceptance criteria | Notes |
|---|---|---|
| β | A block SDT containing supported rich/image placeholder content retains that content after template export and reload. | The PR adds DOCX exporter changes that create media targets for data-URI images and ensures relationships are written; tests perform an export β re-import and assert the block contains an image media part and the reopened document has the image. |
| β | A block SDT containing text placeholder content retains that content after document export and reload (sanity test). | Round-trip tests insert block text SDTs, export and re-import, and assert text content is preserved. |
| β | An inline SDT containing text placeholder content retains that content after document export and reload (sanity test). | The same round-trip suite covers inline structured content insertion and verifies the reopened inline SDT text matches the original. |
| β | The saved DOCX contains the block SDT wrapper and the expected child content under w:sdtContent (i.e., the media child is written under w:sdtContent). | Tests inspect the serialized document.xml and check for w:sdt/w:sdtContent and that it contains drawing/blip entries for the image; exporter now maps data-URI to word/media/* package paths and writes relationships. |
| β | Reopening the saved template shows the same visible signature/image content inside the block SDT. | The round-trip tests re-import the exported DOCX into a new editor instance and assert the reopened structured-content block has an image node with a word/media/* src and same alt/size attributes. There is also a paint-only test that repaints saved model and asserts the painted DOM img uses the original data-URI. |
| β | Regression coverage verifies the block remains and the image/rich content remains across export/import or model repaint. | The PR adds an extensive test suite covering preset-content insertion β paint β export β re-import for SVG (base64 and percent-encoded) and PNG, along with unit/browser tests for painter, image registration, importer helpers, and exporter behavior to prevent regressions. |
Validate base64 payloads in isValidImageDataUrl before treating them as renderable or exportable. Previously any data:image/*;base64 URL was accepted regardless of payload contents, so malformed sources could reach the DOCX exporter and produce unreadable media parts.
Summary
Images set as
presetContentinside a structured-content (SDT) field β most commonly SVG signatures and other preset graphics β were being dropped on DOCX export. The root cause was a chain of assumptions about data-URI images that only held for inline-pasted, base64 PNG/JPEG content:base64,payloads, so URL-encoded SVGs (the format produced by most signature widgets) were rejected at paint time;src.split('word/')[1], which returnsundefinedfor adata:URI, so the relationship target was never written and the<a:blip>lost its image reference;This PR consolidates the data-URI policy into
shared/url-validation, threads it through the painter, importer, registration plugin, and DOCX exporter, and adds a roundtrip test covering preset-content insertion β paint β export β re-import.Highlights
Shared data-URI policy (
shared/url-validation)getDataUriMetadata,tryDecodeDataUriText,isValidImageDataUrl, plusIMAGE_DATA_URL_MIME_TYPESandMAX_IMAGE_DATA_URL_LENGTH.image/svg+xml, and only when the percent-encoded text decodes successfully. 10 MB cap is enforced uniformly.Painter (
layout-engine/painters/dom)isValidImageDataUrl. Removed the localVALID_IMAGE_DATA_URLregex /MAX_DATA_URL_LENGTHconstant.Image registration plugin (
super-editor/extensions/image)getDataUriDecodedByteLengthenforces the upload byte cap before in-place registration (handles both base64 and percent-encoded payloads).DOCX exporter (
v3/handlers/wp/helpers/decode-image-node-helpers.js)createMediaTargetForDataUriallocates a stableword/media/image-<hash>.<ext>package path for each data-URI source, caches the mapping per export (params.dataUriMediaTargets), and resolves rId collisions by appending a random suffix when two distinct sources hash to the same path.resolveImageRelationshipId+getImageRelationshipLookup).pm-adapter
resolveNodeSdtMetadatais now generic in its override type; callers likefieldAnnotationNodeToRunget the precise metadata type without a cast.structuredContentmetadata only β other inline SDT variants no longer collapse into a placeholder when empty.Importer/helpers cleanup
helpers.js#dataUriToArrayBufferaccepts URL-encoded SVG payloads.image-dimensions.jsreads SVG intrinsic dimensions from<svg width/height>attributes (base64 and percent-encoded).simpleStringHash/stableHexHashhelpers moved tocore/utilities/hash.js;documentCommentsImporterandhandleBase64now reuse them.mediaHelpers.jscentralizes MIME β extension mapping (getImageExtensionFromMimeType) and re-exports shared metadata helpers with the extension annotation.Tests
sd-3116-structured-content-image-roundtrip.test.jsβ 453 LOC suite covering preset-content insertion, paint, save, and re-import for both base64 and percent-encoded SVG signatures plus PNG cases.structured-content-commands.test.jsβ verifiesinsertStructuredContentBlockregisters the preset image in media and rewritessrcto aword/media/...path.imageRegistrationPlugin.browser.test.jsβ in-place SVG registration, parent-store mirroring, and upload cap.handleBase64.test.js,image-dimensions.test.js,mediaHelpers.test.js,helpers.test.js,decode-image-node-helpers.test.jsβ coverage for non-base64 SVG paths, malformed payloads, MIME normalization, and exporter target collisions.painters/dom/src/index.test.tsβ non-base64 SVG rendering for bothimageruns and field annotations.Risk / compatibility
word/media/image-*.svgparts.editor.options.parentEditorβ unchanged for standalone editors.