Guides work on SVG import into the Grida Canvas Rust engine (cg crate). Covers crates/grida-canvas/src/svg/, the grida-dev svg-to-grida CLI, cross-boundary FBS codec tests (Rust encode → TS decode), SVG fixture authoring, and known SVG import limitations (text model, filters, transforms). Use when adding SVG feature support, fixing import bugs, authoring SVG test fixtures, debugging cross-boundary codec failures, or investigating what SVG elements map to which Grida node types.
Crate: crates/grida-canvas/src/svg/
fixtures/test-svg/L0/.svg bytes
→ usvg::Tree::from_data() — parse + resolve (third_party/usvg/)
→ from_usvg_tree::* — usvg::Tree → Grida scene graph
→ pack::* — pack nodes into IPackedSceneDocument
→ io::archive::pack() — produce .grida ZIP
Key design: SVG import is Rust-only. There is no TypeScript path for SVG→Grida.
The TypeScript cross-boundary test (fbs-svg-cross-boundary.test.ts) decodes .grida files that were generated by Rust via svg-to-grida, not a TS converter.
| Path | Role |
|---|---|
crates/grida-canvas/src/svg/from_usvg_tree.rs | Core conversion: usvg nodes → Grida nodes |
crates/grida-canvas/src/svg/pack.rs | Packs converted nodes into scene document |
crates/grida-canvas/src/svg/from_usvg.rs | High-level entry: bytes → scene |
crates/grida-canvas/src/svg/sanitize.rs | Pre-processing / sanitization |
crates/grida-dev/src/main.rs | svg-to-grida subcommand |
fixtures/test-svg/L0/ | Committed SVG fixtures |
fixtures/test-svg/.generated/ | Gitignored, generated .grida outputs |
packages/grida-canvas-io/__tests__/fbs-svg-cross-boundary.test.ts | TS-side codec test |
crates/grida-canvas/AGENTS.md for crate conventions and commands.docs/wg/feat-svg/text-import.md before touching text conversion — the text model is intentionally limited and the design is documented there.from_usvg_tree.rs.third_party/usvg/src/.from_usvg_tree.rs (the main convert_* functions).fixtures/test-svg/L0/ that exercises the feature.# Step 1: Rust encodes all L0 SVG fixtures → .grida files
cargo run -p grida-dev -- svg-to-grida fixtures/test-svg/L0
# Step 2: TS decodes the .grida files and runs assertions
pnpm vitest run fbs-svg-cross-boundary --reporter=verbose
Outputs land in fixtures/test-svg/.generated/ (gitignored).
For custom SVG files:
cargo run -p grida-dev -- svg-to-grida path/to/svgs -r
# W3C SVG test suite (requires separate download — see docs/wg/feat-svg/testing.md)
cargo run -p grida-dev --release -- reftest path/to/w3c-suite/
# resvg test suite
cargo run -p grida-dev --release -- reftest path/to/resvg-test-suite/
See crates/grida-dev/TESTING.md for full reftest flags.
cargo test -p cg
cargo test -p cg svg # filter to SVG tests only
<rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, <path><g> with transforms<use> / <defs> (resolved by usvg)TextSpanNodeRec per usvg TextChunk (see text model below)SVG text is chunk-based: <text> → GroupNodeRec, each TextChunk → TextSpanNodeRec.
What is lost: inline style variation within a line, per-character x/y lists, baseline-shift, text-decoration per span, text-on-path.
This matches Figma's SVG import fidelity. See docs/wg/feat-svg/text-import.md for full details before changing text conversion.
<pattern> fill (partially tracked in docs/wg/feat-svg/pattern.md)<textPath> (text on path)<animate>, SMIL)fixtures/test-svg/L0/ — one file per feature.<feature>.svg (e.g. stroke-dasharray.svg, transforms-nested.svg)..generated/ are gitignored; regenerate on demand with svg-to-grida.When fixing a codec bug found via the cross-boundary test:
it(...) assertion in fbs-svg-cross-boundary.test.ts — one assertion per bug.it.fails(...) with a comment explaining why.# Rust check + tests
cargo check -p cg --all-targets
cargo test -p cg
# Cross-boundary cycle
cargo run -p grida-dev -- svg-to-grida fixtures/test-svg/L0
pnpm vitest run fbs-svg-cross-boundary