Export to SVG, PNG blob or canvas through an async command/lastExport round-trip correlated by id.

Export round-trip

Export to SVG, PNG blob or canvas through an async command/lastExport round-trip correlated by id.


Overview

Excalidraw's export utilities are async. The wrapper models the round-trip as (1) Python writes command, (2) JS awaits the export and writes lastExport with the same id, (3) Python reads the result.

Live demo

Source

# File: docs/export/export.py

"""Export: drive exportToSvg / exportToBlob / exportToCanvas via command."""

import uuid

import dash
import dash_mantine_components as dmc
from dash import Input, Output, State, callback, html, no_update

from dash_excalidraw import DashExcalidraw
from docs._shared import (
    canvas_frame,
    code_block,
    sync_canvas_theme,
    two_column,
)

sync_canvas_theme("export-canvas")

CODE = """
# Python -> component: dispatch an export command
return {'id': str(uuid.uuid4()),
        'type': 'exportToSvg',
        'payload': {'exportPadding': 20}}

# component -> Python: observe lastExport and match ids
@callback(Output('preview', 'children'),
          Input('canvas', 'lastExport'))
def preview(result):
    if result and result['type'] == 'exportToSvg':
        return html.Iframe(srcDoc=result['result'])
"""


component = dmc.Stack(
    gap="md",
    children=[
        code_block(CODE),
        two_column(
            canvas_frame(
                DashExcalidraw(
                    id="export-canvas",
                    height="520px",
                    initialData={
                        "elements": [
                            {
                                "id": "s1",
                                "type": "rectangle",
                                "x": 150,
                                "y": 150,
                                "width": 220,
                                "height": 140,
                                "angle": 0,
                                "strokeColor": "#2f9e44",
                                "backgroundColor": "#d3f9d8",
                                "fillStyle": "solid",
                                "strokeWidth": 2,
                                "roughness": 1,
                                "opacity": 100,
                                "seed": 7,
                                "version": 1,
                                "versionNonce": 7,
                                "isDeleted": False,
                                "groupIds": [],
                                "frameId": None,
                                "boundElements": [],
                                "updated": 1,
                                "link": None,
                                "locked": False,
                            }
                        ],
                        "scrollToContent": True,
                    },
                ),
                min_height=520,
            ),
            dmc.Stack(
                gap="sm",
                children=[
                    dmc.Paper(
                        withBorder=True,
                        p="md",
                        radius="md",
                        children=dmc.Stack(
                            gap="sm",
                            children=[
                                dmc.Text("Dispatch", size="sm", fw=600, c="dimmed"),
                                dmc.Group(
                                    [
                                        dmc.Button(
                                            "Export SVG",
                                            id="export-svg-btn",
                                            variant="light",
                                        ),
                                        dmc.Button(
                                            "Export PNG",
                                            id="export-png-btn",
                                            variant="light",
                                            color="teal",
                                        ),
                                    ]
                                ),
                            ],
                        ),
                    ),
                    dmc.Paper(
                        withBorder=True,
                        p="md",
                        radius="md",
                        children=dmc.Stack(
                            gap="sm",
                            children=[
                                dmc.Text("Preview", size="sm", fw=600, c="dimmed"),
                                dmc.Box(id="export-preview", style={"minHeight": 180}),
                            ],
                        ),
                    ),
                ],
            ),
        ),
    ],
)


@callback(
    Output("export-canvas", "command"),
    Input("export-svg-btn", "n_clicks"),
    Input("export-png-btn", "n_clicks"),
    prevent_initial_call=True,
)
def _dispatch_export(_svg, _png):
    trigger = dash.ctx.triggered_id
    if trigger == "export-svg-btn":
        return {
            "id": f"svg-{uuid.uuid4()}",
            "type": "exportToSvg",
            "payload": {"exportPadding": 20},
        }
    if trigger == "export-png-btn":
        return {
            "id": f"png-{uuid.uuid4()}",
            "type": "exportToBlob",
            "payload": {"mimeType": "image/png", "exportPadding": 20},
        }
    return no_update


@callback(
    Output("export-preview", "children"),
    Input("export-canvas", "lastExport"),
)
def _render_preview(result):
    if not result or "result" not in result:
        return dmc.Text("(dispatch an export to preview)", c="dimmed", size="sm")
    export_type = result.get("type")
    payload = result.get("result")
    if not payload:
        err = result.get("error") or "empty result"
        return dmc.Alert(color="red", variant="light", children=str(err))
    if export_type == "exportToSvg":
        return html.Iframe(
            srcDoc=payload,
            style={
                "width": "100%",
                "height": "260px",
                "border": "0",
                "background": "white",
            },
        )
    if export_type in ("exportToBlob", "exportToCanvas"):
        return html.Img(
            src=payload,
            style={"maxWidth": "100%", "maxHeight": "260px", "display": "block"},
        )
    return dmc.Text(f"Unhandled export type: {export_type}", c="dimmed")

:defaultExpanded: false :withExpandedButton: true


Source: /export

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: