| name | io-svg |
| description | Guides work on SVG import into the Grida Canvas Rust engine (grida crate). Covers crates/grida/src/import/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.
|
SVG I/O — Rust SVG Import Pipeline
Crate: crates/grida/src/import/svg/
When to Use This Skill
- Adding support for new SVG elements or attributes
- Fixing SVG-to-Grida conversion bugs (wrong transform, wrong paint, etc.)
- Authoring new SVG test fixtures in
fixtures/test-svg/L0/
- Debugging cross-boundary codec failures (Rust encodes ≠ TS decodes)
- Understanding what SVG features are supported vs. dropped
- Investigating text import fidelity
Architecture
.svg bytes
→ usvg::Tree::from_data() — parse + resolve (third_party/usvg/)
→ packed_scene::* — 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.
Key Files
| Path | Role |
|---|
crates/grida/src/import/svg/packed_scene.rs | Core conversion: usvg nodes → Grida nodes |
crates/grida/src/import/svg/pack.rs | Packs converted nodes into scene document |
crates/grida/src/import/svg/from_usvg.rs | High-level entry: bytes → scene |
crates/grida/src/formats/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 |
Common Tasks
Orient before touching code
- Read
crates/grida/AGENTS.md for crate conventions and commands.
- Read
docs/wg/feat-svg/text-import.md before touching text conversion — the text model is intentionally limited and the design is documented there.
- Grep for the relevant element in
packed_scene.rs.
Add support for a new SVG element or attribute
- Find where usvg exposes the element in
third_party/usvg/src/.
- Add the mapping in
packed_scene.rs (the main convert_* functions).
- Add a minimal SVG fixture to
fixtures/test-svg/L0/ that exercises the feature.
- Run the cross-boundary cycle (see below) to verify Rust→TS round-trip.
Run the cross-boundary codec cycle
cargo run -p grida_dev -- svg-to-grida fixtures/test-svg/L0
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
Run SVG reftests
cargo run -p grida_dev --release -- reftest path/to/w3c-suite/
cargo run -p grida_dev --release -- reftest path/to/resvg-test-suite/
See crates/grida_dev/TESTING.md for full reftest flags.
Rust tests for SVG
cargo test -p grida
cargo test -p grida svg
SVG Feature Coverage
Fully supported
- Basic shapes:
<rect>, <circle>, <ellipse>, <line>, <polyline>, <polygon>, <path>
- Groups
<g> with transforms
- Fills and strokes (solid color, linear gradient, radial gradient)
- Clip paths and masks
- Filters: drop-shadow, blur, color-matrix, lighting, compositing primitives
- Opacity and blend modes
<use> / <defs> (resolved by usvg)
- Text: one Grida
TextSpanNodeRec per usvg TextChunk (see text model below)
- Stroke dash arrays
- Nested transforms
Text model (important)
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.
Known gaps / unsupported
<pattern> fill (partially tracked in docs/wg/feat-svg/pattern.md)
<textPath> (text on path)
- Animations (
<animate>, SMIL)
- CSS stylesheets inside SVG
Fixture Conventions
- Committed fixtures live in
fixtures/test-svg/L0/ — one file per feature.
- Naming:
<feature>.svg (e.g. stroke-dasharray.svg, transforms-nested.svg).
- Keep fixtures minimal — isolate one feature per file.
- Generated outputs in
.generated/ are gitignored; regenerate on demand with svg-to-grida.
Cross-Boundary Test Conventions
When fixing a codec bug found via the cross-boundary test:
- Add a targeted
it(...) assertion in fbs-svg-cross-boundary.test.ts — one assertion per bug.
- Do NOT use snapshot comparisons — assert the specific field that was wrong.
- If the feature is a known limitation (e.g. scale/skew in transforms), mark the test
it.fails(...) with a comment explaining why.
Verification After Changes
cargo check -p grida --all-targets
cargo test -p grida
cargo run -p grida_dev -- svg-to-grida fixtures/test-svg/L0
pnpm vitest run fbs-svg-cross-boundary