Skip to main content

Crate excalidraw_render

Crate excalidraw_render 

Source
Expand description

Renders Excalidraw scenes to SVG, PNG and PDF, matching Excalidraw 0.18.1’s exporter as closely as possible.

It is pure Rust with every font bundled: rendering needs no browser, Node.js, network access or installed fonts. Export a whole scene or each frame on its own. Every render also returns a compatibility report that lists what the renderer substituted, omitted or approximated, so differences from Excalidraw are reported rather than silent.

This release reproduces Excalidraw 0.18.1. In 0.x, any release may change output and diagnostics; pin the renderer version and dependency lockfile for reproducible output. Linux x86-64 GNU is release-qualified; browser WASM is experimental.

Throughout this documentation, reference means Excalidraw’s own exporter at the pinned release, v0.18.1: “reference frame-export semantics” and “reference dark-mode colors” describe what Excalidraw itself produces. An admitted scene is one SceneDocument::parse accepted.

§Quick start

Admit the scene once with SceneDocument::parse, describe one output with a RenderRequest, and render it with a Renderer. A successful render returns the complete Artifact together with a CompatibilityReport listing every repair, substitution or omission the renderer made to keep the artifact meaningful; an empty report means none were needed.

use excalidraw_render::{
    FrameRenderRequest, OutputFormat, RenderRequest, Renderer, SceneDocument, Sizing,
};

// The contents of an `.excalidraw` file.
let bytes = br##"{
    "type": "excalidraw",
    "version": 2,
    "elements": [
        {"id": "overview", "type": "frame", "name": "Overview",
         "x": 0, "y": 0, "width": 200, "height": 140},
        {"id": "box", "type": "rectangle", "frameId": "overview",
         "x": 40, "y": 30, "width": 120, "height": 80}
    ],
    "appState": {"viewBackgroundColor": "#ffffff"},
    "files": {}
}"##;
let scene = SceneDocument::parse(bytes)?;
let renderer = Renderer::new();

// Export the whole scene as SVG with the reference export defaults.
let request = RenderRequest::builder(OutputFormat::Svg).build()?;
let outcome = renderer.render(&scene, &request)?;
for diagnostic in outcome.report().diagnostics() {
    eprintln!(
        "{} {} at {}: {}",
        diagnostic.severity(),
        diagnostic.code(),
        diagnostic.json_pointer(),
        diagnostic.message()
    );
}
let svg = outcome.artifact().bytes();
assert!(svg.starts_with(b"<svg"));

// Export every frame as a PNG no larger than 1024 pixels on either side.
let request = FrameRenderRequest::builder(OutputFormat::Png)
    .sizing(Sizing::MaxDimension(1024.0))
    .build()?;
for frame in scene.frames() {
    let outcome = renderer.render_frame(frame, &request)?;
    let png = outcome.artifact().bytes();
    println!("{}: {} bytes", frame.name().unwrap_or("unnamed"), png.len());
    assert!(png.starts_with(b"\x89PNG"));
}

Each failure type (SceneParseError, RequestError, RenderError, FrameBatchError) carries a stable ErrorCode, and descriptor() gives its serializable form; the first three also carry typed details. Renderer::render_frames renders a frame batch that shares scene preparation across frames.

§Serving untrusted input

The renderer is built to render scene documents from untrusted sources, but a service still has to decide how much CPU time and memory one request may take.

  • Share and move freely. Renderer and SceneDocument are immutable, Clone, Send and Sync; one renderer can serve every request concurrently, and an admitted scene can be rendered to several formats from different threads.

  • Rendering is synchronous and CPU-bound. In an async server, run it on a blocking pool (for example tokio::task::spawn_blocking) rather than on the executor’s worker threads.

  • There is no deadline or cancellation. Every render terminates, because all work is bounded by ResourceLimits, but a render cannot be interrupted once started. When you need a hard wall-clock deadline, render in a separate process you can kill.

  • Bound raster sizes from the request. For PNG and PDF, prefer Sizing::MaxDimension over a caller-chosen Sizing::Scale so a large scene cannot request an enormous PNG or page; then lower max_raster_dimension and max_raster_surface_pixels to what you are willing to allocate. SVG ignores MaxDimension and keeps the scene’s logical size, which max_svg_dimension bounds instead.

  • max_working_bytes is not a memory cap. It budgets the renderer’s deterministic, logical working set so that the same input fails the same way on every machine; it does not measure or limit allocator usage. Actual peak memory is higher, dominated by raster surfaces: PNG rasterizes a 4× supersampled canvas, about 65 bytes per output pixel, so the default max_raster_surface_pixels (33,554,432) admits renders that need over 2 GiB. A ceiling of 4,194,304 pixels (2048 × 2048) keeps one render near 300 MB. Size worker memory (or a container limit) for your raster ceilings and concurrency.

  • Parsing has its own ceilings. SceneDocument::parse accepts up to 64 MiB of JSON whatever max_scene_bytes says; that limit is checked when rendering. Bound request bodies before parsing for a lower ceiling.

  • Limits apply per render. Renderer::render_frames renders every selected frame within its own limits, so bound the frame count of an untrusted batch.

  • Build with optimizations. Unoptimized builds are roughly 50 times slower, which turns ordinary scenes into multi-second renders and makes resource limits much easier to exhaust in wall-clock terms. Most of the time goes to dependencies (rasterization, compression, font decoding), so in a debug build of your service optimize every dependency:

    [profile.dev.package."*"]
    opt-level = 2

§Cargo features

All three features are on by default. The API is the same with any combination of them, so Cargo’s feature unification can never change what compiles; a feature only decides which requests a build can serve, and turning one off never changes the bytes of the output that remains.

  • png renders OutputFormat::Png. It brings the resvg rasterizer and tiny-skia.
  • pdf renders OutputFormat::Pdf. It brings the Krilla PDF writer, and the rasterizer for the isolated raster fallbacks of embedded SVG effects.
  • cjk bundles Xiaolai, Excalifont’s Chinese, Japanese and Korean fallback font (about 12 MiB). Without it, a render whose text needs Xiaolai fails with ErrorCode::RENDER_FONT_UNAVAILABLE (render.font-unavailable, a RenderErrorCategory::Capability failure) rather than substituting another font. Every other font, including Noto Emoji, is always bundled.

SVG output is always available. Without png or pdf, building a RenderRequest or FrameRenderRequest for that format fails with ErrorCode::REQUEST_FORMAT_UNAVAILABLE (request.format-unavailable), whose message names the feature to enable. An SVG-only build, for example for WebAssembly, depends on the renderer like this:

[dependencies]
excalidraw-render = { version = "0.1", default-features = false, features = ["cjk"] }

Structs§

Artifact
One complete renderer-owned static export.
AssetChainEntry
One entry in a nested asset occurrence chain.
CompatibilityDiagnostic
Stable serializable view of one compatibility diagnostic occurrence.
CompatibilityReport
Complete ordered compatibility diagnostics for one render transaction.
DiagnosticCode
String-backed stable compatibility diagnostic code.
ErrorCode
String-backed stable error code.
ErrorDescriptor
Stable serializable view of a typed renderer failure.
FiniteValue
Finite schema-1 number with canonical JSON serialization.
FrameBatchError
Failure to construct a lazy multi-frame render operation.
FrameOptions
Whole-scene frame rendering controls.
FrameRef
Borrowed identity and display metadata for one live frame occurrence.
FrameRefs
Iterator over live ordinary and magic frame occurrences.
FrameRenderCursor
Lazy source-ordered rendering of selected frames from one scene document.
FrameRenderEntry
One target result yielded by a lazy multi-frame render operation.
FrameRenderRequest
A validated one-format frame render request.
FrameRenderRequestBuilder
Builder for a validated one-format frame render request.
LogicalBounds
Finite logical bounds of a PDF raster fallback closure.
MetadataOptions
Optional artifact metadata controls.
NestedPath
Canonical identity path within one nested source.
NestedPathSegment
One segment of a canonical nested-source path.
PdfOptions
PDF-specific request controls.
RenderError
Fatal render failure with the complete report accumulated before failure.
RenderOutcome
Successful result of one complete render transaction.
RenderRequest
A validated one-format render request.
RenderRequestBuilder
Builder for a validated one-format render request.
Renderer
Immutable renderer for this release’s compatibility revision.
RequestError
Indicates that a render request could not be validated.
ResourceLimits
Validated semantic resource ceilings for one render.
ResourceLimitsBuilder
Builder for validated semantic resource ceilings.
SceneDocument
An admitted Excalidraw scene document.
SceneParseError
Indicates that scene-document bytes could not be admitted.
SupplementalAsset
One owned caller-supplied asset.
SupplementalAssets
Owned caller-supplied assets keyed by scene file ID.
SupplementalAssetsBuilder
Builder for an owned deterministic supplemental-asset mapping.

Enums§

ColorMode
Color mode used to interpret scene colors.
DiagnosticDetails
Typed details attached to an ErrorDescriptor or a CompatibilityDiagnostic.
DiagnosticEffect
Observable effect class of a compatibility diagnostic.
DiagnosticSeverity
Severity of one compatibility diagnostic occurrence.
ErrorFamily
Stable family of a typed renderer failure.
FrameBatchErrorKind
Stable category of a lazy frame-batch construction failure.
FrameKind
Kind of live frame occurrence discovered in a scene document.
Measurement
Stable attempted or lower-bound measurement.
MetadataComponent
Optional artifact metadata component.
MetadataOperation
Operation performed on an optional metadata component.
NestedSegmentKind
Kind of one canonical nested-source path segment.
OutputFormat
The single artifact format produced by a render request.
PdfRasterFallback
Policy for PDF-only isolated raster fallback.
Recovery
Stable recovery applied after diagnosed compatibility loss.
RenderErrorCategory
Stable category of a fatal render failure.
ResourceValue
Nonnegative integer or finite number used by semantic resource details.
Sizing
Output sizing selected for one render.
SvgFontSubsetStage
Stable stage vocabulary for a direct-SVG font subset failure.
ValueKind
Unit used by a stable limit measurement.

Functions§

license_bundle
Returns the complete package-wide license and attribution bundle.

Type Aliases§

ResourceMeasurement
Stable measurement form for integer, dimension, or coordinate resources.