Images & drawings
Inline and floating DrawingML pictures in DOCX: supported formats, wrap modes, authoring them from React, accessibility, and the security boundaries.
DrawingML pictures (w:drawing with pic:pic) lay out, paint, and round-trip through the engine. React ships insert, wrap, properties, alt text, and selection-overlay authoring.
Supported formats
| Format | Insert (React) | Layout & paint | Round-trip |
|---|---|---|---|
| PNG, JPEG, GIF | Yes, normalizeImageBytes preflight | Full decode at authored wp:extent | Original media preserved unless replaced |
| BMP, WebP | No insert | Full decode at authored wp:extent | Original media preserved |
| SVG | No insert | Painted at authored wp:extent | Original media preserved |
| TIFF, EMF, WMF | No insert | Rasterized when conversion succeeds; labeled placeholder otherwise | Preserved; no zero-click fetch |
External r:link / TargetMode="External" | No auto-load | Placeholder + preserved rel | Preserved; explicit user gesture to embed |
Insert accepts PNG/JPEG/GIF only. Other payloads stay in the package and participate in pagination through their authored extent.
BMP and WebP decode in the browser and paint like PNG or JPEG. BMP support includes top-down
bitmaps and the 12-byte BITMAPCOREHEADER. WebP support includes lossy, lossless, and extended
containers.
TIFF, EMF, and WMF paint when conversion to a validated raster succeeds. The original media stays unchanged in the package through save. When conversion is unavailable, declined, or fails, the image keeps its extent and shows a labeled placeholder.
SVG is painted through an <img>, which puts the browser in secure static mode: scripts in the
file never run and external references in it are never fetched. Its intrinsic size is read from
width/height/viewBox for reset-to-natural-size; layout always uses the authored wp:extent.
An SVG whose root element cannot be read renders as a placeholder rather than being guessed at.
Reading selection state
Use the canonical read model shared by snapshot and imperative API:
import { useEditorState } from '@docx-editor.dev/react';
const image = useEditorState((s) => s.image);
// SelectedImageState | null: widthEmu/heightEmu, crop, wrap, position, locks, resourceStatuseditor.getSelectedImage(); // same SelectedImageState shape
editor.snapshot().image; // reference-stable until selection or image fields moveSelectedImageState.wrap reports all nine Word menu targets (inline, square, squareLeft, squareRight, tight, through, topAndBottom, behind, inFront). behind and inFront are both wrapNone with different @behindDoc.
React authoring surface
Default toolbar slots (contextual image group when a picture is selected):
| Slot | Component / hook | Engine command |
|---|---|---|
image.insert | ImageInsertProvider, ImageInsertTrigger | executeImageCommand({ type: 'insertImage', … }) |
image.wrap | ImageWrap, useEditorValueCommand('image.wrap') | setImageWrapType via runToolbarCommand |
image.altText | ImageAltText | setImageAltText |
image.properties | ImagePropertiesTrigger, DocxEditorImagePropertiesDialog | setImageProperties |
Insert preflight:
import { normalizeImageBytes } from '@docx-editor.dev/react';
const result = normalizeImageBytes(bytes);
if (result.ok) {
await editor.executeImageCommand({
type: 'insertImage',
data: result.bytes,
mime: result.mime,
widthPoints: result.widthPoints,
heightPoints: result.heightPoints,
});
}Floating pictures: select in the canvas, drag to move (setImagePosition), eight handles or Alt+Arrow to resize (setImageProperties), wrap menu for all nine choices. One undo step per completed gesture.
Wrap modes and text reflow
Wrap changes are layout-structural: exclusion zones feed line breaking before paint. Square/tight/through use authored polygons (with distances and effectExtent); top-and-bottom and square variants reflow body text accordingly. Header/footer rule: page-relative anchored letterheads do not inflate the header box. Body flow height still sizes the furniture band.
Accessibility
Alt text uses @descr, then @title; @name is never announced. A picture with neither description nor title is exposed as decorative. Hidden drawings (@hidden) suppress paint, hit-testing, and handles while remaining preserved on save.
Security and external images
- No network fetch on open, layout, paint, or save for external relationships.
- Embedded bytes are signature-checked and dimension-capped before decode.
- Hyperlinks on drawings (
a:hlinkClick) require an explicit gesture and pass throughsanitizeHref. - Optional explicit download-and-embed path for external targets is user-initiated only (bounded size/content-type).
Current limits
Partially rendered:
- Charts, SmartArt, groups, canvases: extent + labeled placeholder
- Anchored text boxes render their story read-only, clipped to the extent (page-number fields inside header/footer text boxes evaluate per page); inline text boxes, linked chains, autofit, and rotation stay placeholders
Preserved inertly:
- VML (
w:pict); watermark rendering and editing remain planned w:object/w:altChunk: generic preservation with diagnostics- Tracked-change accept/reject on drawings
Not rendered and not guaranteed to round-trip:
- Artistic effects (
a:effectLst)
Next steps
- Toolbar customization: override
DocxEditor.Toolbar.ImageWrapand siblings - Word fidelity matrix: live support claims
- Headers & footers: letterhead anchors in furniture bands
Headers & footers
Edit DOCX headers and footers in place, insert page fields, and use per-section first-page and even-page variants.
Custom styles & branding
Use a branded DOCX template to control headings, fonts, and table styles. Documents inherit your styles automatically and preserve them when saved.