Output
PNG rendering requires Flutter, even without a mounted canvas. It is not a standalone Dart server renderer.
session.exportPng
dart
Future<DiagramPngSnapshot> exportPng(
DiagramPngOptions options, {
ImageProvider<Object> Function(DiagramImageSource)? imageProvider,
})| Argument | Contract |
|---|---|
options | Required positional crop, raster scale, background, and image-loading timeout. |
imageProvider | Optional resolver for authenticated or application-owned images. |
The publication is captured when called, before asynchronous image loading.
DiagramPngOptions
dart
const DiagramPngOptions({
required DiagramRect bounds,
double pixelRatio = 1,
DiagramColor? background,
Duration resourceTimeout = const Duration(seconds: 30),
})| Property | Default | Contract |
|---|---|---|
bounds | Required | Finite world-space crop with positive width and height. Negative world positions are allowed. |
pixelRatio | 1 | Finite, positive raster scale; does not reflow text. |
background | null | Transparent when omitted. |
resourceTimeout | 30 seconds | Positive image-loading timeout, not a deadline for the complete render. |
Options are immutable. Validation occurs during export, not in the const constructor.
DiagramPngSnapshot
| Property | Type | Contract |
|---|---|---|
bytes | Uint8List | Copied, unmodifiable PNG bytes. |
documentId | String | Captured document identity. |
revision | int | Captured document revision. |
textLayoutRevision | int | Captured scene's text-layout revision. |
bounds | DiagramRect | Requested world crop. |
width | int | ceil(bounds.width * pixelRatio) pixels. |
height | int | ceil(bounds.height * pixelRatio) pixels. |
Example
Export a captured document without mounting a canvas:
dart
final snapshot = await session.exportPng(
const DiagramPngOptions(
bounds: DiagramRect.fromLTWH(0, 0, 1200, 800),
pixelRatio: 2,
background: DiagramColor(0xffffffff),
),
);
// Hand these bytes to your application's save/share/download integration.
final pngBytes = snapshot.bytes;Bounds are an explicit world-space crop. Pixel ratio scales raster resolution without changing text wrapping or layout. Omitting the background produces transparency. An empty document produces an empty crop with the requested background. Output is limited to 8192 pixels per side and 16 megapixels overall; invalid bounds/scales and oversized requests throw before raster allocation.
The result contains immutable PNG bytes, pixel dimensions, world bounds, document ID, document revision and text-layout revision. It captures the current publication when called, including any currently published preview. Later edits, navigation or session disposal do not retarget an in-flight export. Calling it after disposal fails.
The same scene painter renders canonical shapes, connectors, labels, ports and clipping at full detail. Grid, selection, hover, text-entry placeholders and editor overlays are excluded. Runtime animation effects and arbitrary Flutter widget overlays are not canonical document content and are not included.
Images and fonts
Required images share the canvas image-resolution implementation. Export waits for them and rejects failed loads or a resource timeout (30 seconds by default), rather than painting a loading placeholder. A host can pass imageProvider to supply an authenticated or application-owned Flutter ImageProvider for each DiagramImageSource.
Text must already have Flutter-shaped line and marker geometry in the captured scene. Normal DrawingSession instances provide this. A borrowed headless store with unshaped text is rejected. Load application fonts before constructing or refreshing the session; export preserves the captured shaping, including any font fallback already present, and does not silently reshape it after an asynchronous font load.
Failures
| Condition | Result |
|---|---|
| Call after session disposal | Synchronous StateError. |
| Invalid crop, scale, timeout, or excessive pixel allocation | Future fails with ArgumentError. |
| Mismatched publication or missing Flutter text geometry | Future fails with StateError. |
| Image loading fails | Future propagates the loading failure. |
| Image loading exceeds the timeout | Future fails with TimeoutException. |
| PNG encoder returns no bytes | Future fails with StateError. |
Rendering failures propagate. Temporary render resources are released on success or failure.
exportDiagramPng
The editor package also accepts an already captured publication:
dart
Future<DiagramPngSnapshot> exportDiagramPng(
DiagramPublication publication, {
required DiagramPngOptions options,
ImageProvider<Object> Function(DiagramImageSource)? imageProvider,
})The publication must contain matching canonical state and resolved geometry. Options, result, and asynchronous failure contracts match the session method. This function also requires Flutter.
Scope and verification
Snapshot lifetime, basic pixels, transparency, scaling, empty output and rejection behavior have automated coverage. A release-Wasm browser diagnostic verifies basic PNG generation, decoding, and pixels. Exhaustive nested/rotated/clipped content fidelity and host download integration remain open. Complete vector export, automatic document bounds with bleed, animation capture and tiled large-image output are not implemented. The host owns file naming, saving and sharing.
Geometry-only SVG scenes
DiagramSvgScene.encode runs in pure Dart, without a mounted canvas or Flutter renderer:
dart
final svg = DiagramSvgScene.encode(
scene,
bounds: const DiagramRect.fromLTWH(0, 0, 1200, 800),
);| Argument / result | Contract |
|---|---|
scene | Required positional ResolvedScene. Uses its captured geometry and paint order; does not resolve another document or reflow content. |
bounds | Required named DiagramRect; finite, positive world-space crop. |
| Result | Synchronous SVG String, with a transparent background and world-space viewBox. The host owns saving and file naming. |
| Invalid bounds | ArgumentError. |
| Unsupported content | UnsupportedError; no partial document is returned, including when unsupported content is outside the crop. |
Supported content includes shape outlines, fills and styled borders, pressure ink, region fills, dividers, ports, connector bodies and terminal markers. Owner transforms, intersecting ancestor clips and the canvas's separate border/port/marker passes are preserved.
Text (including standalone text and connector labels), image layers and shadows are not supported yet. These reject export rather than disappear or use a different text layout. Unknown resolved element or outline implementations also reject export. Runtime animations, grids, hover and selection are editor presentation and are excluded. This is a limited static vector exporter, not full SVG/PDF output.
SVG geometry adapter
DiagramSvgGeometry in vyuh_diagram_codecs serializes resolved outlines to SVG path data in pure Dart. It is a geometry primitive, not a complete diagram SVG exporter.
| Method | Input | Result |
|---|---|---|
outlinePath(outline) | ResolvedOutline | SVG d data for a rectangle (including fitted circular corners), ellipse, polygon, or open authored path. |
connectorBodyPath(connector) | ResolvedConnector | Visible endpoint-trimmed body polyline in world coordinates; empty if fewer than two body points. |
arrowheadPath(arrowhead) | ResolvedArrowhead | Resolved terminal-marker path, preserving its open/closed contour. Use filled to select fill versus stroke. |
inkHullPath(outline) | AuthoredPathOutline | Closed filled pressure-stroke hull, or an empty string when no hull exists. |
Arcs use SVG arc commands; coordinates are serialized without deliberate decimal rounding. Nonfinite coordinates and nonpositive outline bounds are rejected. Outline implementations outside the closed grammar throw UnsupportedError.
The output uses the same unrotated resolved coordinates as the canvas outline. A complete exporter must still compose owner transforms, ancestor and content clips, styles, connectors, text, images and document metadata. Stroke hulls are separate from authored centerlines so consumers can preserve the canvas's choice of filled pressure geometry or styled centerline. The adapter does not infer missing text shaping or emit a partial diagram while pretending to export it all.
Connector output serializes bodyPoints, exactly as the canvas does. Quadratic and cubic routes therefore retain the resolver's authoritative sampled polyline; the adapter does not rebuild curves from controls or use animation resampling. Marker trimming is already present in that body. Labels, their ink masks, ancestor clipping, color and dash styling still belong to scene composition.
SVG paint attributes
DiagramSvgPaint in vyuh_diagram_codecs supplies immutable SVG attribute maps for geometry composition:
| Method | Input | Result |
|---|---|---|
shapeAttributes(style) | ShapeStyle | Regular surface fill, alpha, stroke width, dash rhythm and caps. Zero-width borders emit stroke="none". |
connectorAttributes(connector) | ConnectorElement | Unfilled connector body stroke, with rounded joins and geometry-appropriate caps. |
Both consume DiagramStrokeStyle.pattern, the same world-space dash/gap grammar used by committed and preview painting. Solid strokes have no dash pattern; dashed strokes use 8/5 with butt caps, and dotted strokes use 1/5 with round caps. Colors retain their alpha. Invalid color ranges and negative or non-finite widths are rejected.
These are composition primitives, not a complete SVG scene exporter. They do not apply transforms, ancestor clips, pressure-ink fill rules, text, labels, images, or decorative layers. Hosts must not apply regular shape surface paint to standalone text or pressure ink.
SVG placement and clips
| Method | Result |
|---|---|
DiagramSvgGeometry.outlineTransform(element) | SVG rotation mapping a resolved framed element's outline to world space. |
DiagramSvgGeometry.ancestorClips(element, scene) | Immutable list of (path, transform) records for clipping ancestors, nearest first. |
Use an element and scene from the same captured publication. Resolved framed sources already contain composed owner placement; applying parent transforms again would position them twice. Each ancestor clip is an independent intersection: nest clip groups instead of putting all paths into one SVG clip (which would union them). Missing or cyclic resolved ownership fails explicitly.
scene.clippingAncestorsOf(element) is also consumed by canvas painting, text/effect clipping, minimaps, accessibility bounds and hit testing. SVG maps those same outlines into world space. This provides placement primitives; complete scene serialization is still pending.