linebender/resvg
An SVG rendering library.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓35+ active contributors
- ✓Distributed ownership (top contributor 27% of recent commits)
Show 4 more →Show less
- ✓Apache-2.0 licensed
- ✓CI configured
- ✓Tests present
- ⚠Scorecard: default branch unprotected (0/10)
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests + OpenSSF Scorecard
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/linebender/resvg)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/linebender/resvg on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: linebender/resvg
Generated by RepoPilot · 2026-05-13 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/linebender/resvg shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 35+ active contributors
- Distributed ownership (top contributor 27% of recent commits)
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Scorecard: default branch unprotected (0/10)
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests + OpenSSF Scorecard</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live linebender/resvg
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/linebender/resvg.
What it runs against: a local clone of linebender/resvg — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in linebender/resvg | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of linebender/resvg. If you don't
# have one yet, run these first:
#
# git clone https://github.com/linebender/resvg.git
# cd resvg
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of linebender/resvg and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "linebender/resvg(\\.git)?\\b" \\
&& ok "origin remote is linebender/resvg" \\
|| miss "origin remote is not linebender/resvg (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "crates/resvg/src/lib.rs" \\
&& ok "crates/resvg/src/lib.rs" \\
|| miss "missing critical file: crates/resvg/src/lib.rs"
test -f "crates/resvg/src/render.rs" \\
&& ok "crates/resvg/src/render.rs" \\
|| miss "missing critical file: crates/resvg/src/render.rs"
test -f "crates/resvg/src/filter/mod.rs" \\
&& ok "crates/resvg/src/filter/mod.rs" \\
|| miss "missing critical file: crates/resvg/src/filter/mod.rs"
test -f "crates/c-api/lib.rs" \\
&& ok "crates/c-api/lib.rs" \\
|| miss "missing critical file: crates/c-api/lib.rs"
test -f "Cargo.toml" \\
&& ok "Cargo.toml" \\
|| miss "missing critical file: Cargo.toml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/linebender/resvg"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
resvg is a fast, memory-safe SVG rendering library written entirely in Rust that converts SVG files to raster images (PNG, etc.). It splits SVG parsing (via the usvg crate) from rendering into separate libraries, enabling flexibility while maintaining correctness across the complex SVG 1.1 specification with a test suite of ~1600 regression tests. Workspace monorepo (Cargo.toml with members: crates/resvg, crates/usvg, crates/usvg/codegen, crates/c-api). Core rendering logic in crates/resvg/src/ with modular filter implementations (filter/box_blur.rs, filter/composite.rs, etc.), geometry handling (geom.rs), clipping (clip.rs), and image handling (image.rs). C bindings in crates/c-api/lib.rs wrap the Rust API; examples in crates/resvg/examples/ (minimal.rs, custom_href_resolver.rs).
👥Who it's for
Systems programmers, embedded developers, and application maintainers who need to render SVG files without shipping a browser or dealing with C/C++ memory unsafety. Also used by developers building CLI tools or WASM-based SVG viewers where binary size and sandboxing matter.
🌱Maturity & risk
Production-ready and actively maintained. The project has been under active development with CI/CD via GitHub Actions (.github/workflows/main.yml, tagged-release.yml), extensive test coverage (1600+ SVG regression tests), dual licensing (Apache-2.0 OR MIT), and targets Rust 1.87.0+. Recent activity and detailed CHANGELOG.md indicate ongoing commits and incremental improvements.
Low risk for core functionality; the monorepo structure (crates/resvg, crates/usvg, crates/c-api) is clean and dependencies are minimal (goal: 'zero bloat', <3MB binary). Risk factors: SVG spec is sprawling (900 pages), so edge-case bugs may surface; C API in crates/c-api/lib.rs adds a secondary interface that must stay in sync; no obvious abandonment signals but maintainer bus factor unclear from file list alone.
Active areas of work
The project is actively maintained with CI on every commit (main.yml workflow), tagged releases (tagged-release.yml workflow), and a structured CHANGELOG.md tracking changes. Based on the Rust 1.87.0+ requirement and MSRV policy, the team regularly updates to track Rust stability. Active work on filter implementations (morphology.rs, turbulence.rs, displacement_map.rs) and test infrastructure.
🚀Get running
git clone https://github.com/linebender/resvg.git
cd resvg
cargo build --release
cargo run --example minimal
Daily commands:
cargo build
cargo run --bin resvg -- input.svg output.png
# Or run examples:
cargo run --example minimal
cargo run --example draw_bboxes
🗺️Map of the codebase
crates/resvg/src/lib.rs— Main entry point for the resvg Rust library; defines the public API and coordinates rendering pipeline.crates/resvg/src/render.rs— Core rendering logic that orchestrates clipping, masking, filters, and rasterization; handles the main render loop.crates/resvg/src/filter/mod.rs— Implements SVG filter effects (blur, color matrix, composite, turbulence, etc.); critical for visual fidelity.crates/c-api/lib.rs— C API bindings layer; enables FFI usage from C/C++ and provides the public C interface contract.Cargo.toml— Workspace configuration defining all member crates, dependencies, and feature flags for the entire project.crates/resvg/src/path.rs— Path parsing and stroking logic; handles bezier curves, line joins, and path rasterization.crates/resvg/src/image.rs— Image loading and rasterization; bridges external image formats into the rendering pipeline.
🛠️How to make changes
Add a new SVG filter primitive
- Create a new filter module file (e.g., crates/resvg/src/filter/custom_effect.rs) with your filter logic (
crates/resvg/src/filter/custom_effect.rs) - Declare the module in crates/resvg/src/filter/mod.rs and add a variant to the dispatch enum (
crates/resvg/src/filter/mod.rs) - Implement the filter function taking a source pixmap, filter region, and parameters; return modified pixmap (
crates/resvg/src/filter/mod.rs) - Add test case(s) in crates/resvg/tests/extra/ with an .svg file and reference .png output (
crates/resvg/tests/extra/my-filter.svg)
Extend the Rust public API with a new method
- Add the method to the appropriate public struct/impl block in crates/resvg/src/lib.rs (e.g., Tree, Render, RenderState) (
crates/resvg/src/lib.rs) - Document the method with rustdoc comments explaining behavior and panics (
crates/resvg/src/lib.rs) - If the method has C bindings, update crates/c-api/lib.rs with a new resvg_*() function wrapper (
crates/c-api/lib.rs) - Rebuild C headers by running cbindgen: the header is auto-generated into crates/c-api/resvg.h (
crates/c-api/resvg.h)
Optimize the rendering pipeline for a specific operation
- Identify the bottleneck in the main render loop in crates/resvg/src/render.rs (
crates/resvg/src/render.rs) - Check if path stroking (crates/resvg/src/path.rs) or clipping (crates/resvg/src/clip.rs) is the culprit (
crates/resvg/src/path.rs) - Optimize the identified module (e.g., cache bounding boxes, batch transforms, parallelize filter convolutions) (
crates/resvg/src/render.rs) - Benchmark against the test suite in crates/resvg/tests/ to ensure visual correctness and measure speedup (
crates/resvg/tests/README.md)
🔧Why these technologies
- Rust — Memory safety without garbage collection; fast native code; zero-cost abstractions for graphics algorithms (bezier, matrix ops, convolutions)
- tiny-skia (rasterizer backend) — Efficient CPU-based rasterization: path filling, stroking, and blending without GPU dependency; portable across platforms
- usvg (SVG parser, separate crate) — Pre-processes SVG tree, resolves CSS styles, flattens transforms; decouples parsing from rendering logic
- C FFI via cbindgen — Exposes Rust library as C API for integration into C/C++ projects, Qt, and other non-Rust consumers
⚖️Trade-offs already made
-
CPU-only rasterization (no GPU acceleration)
- Why: Portability and simplicity; avoids platform-specific graphics APIs (OpenGL, Metal, D3D) and their attendant complexity
- Consequence: Slower for very large SVGs or real-time rendering; suitable for static document rendering and batch conversion
-
Immutable rendering API (Tree consumed for Render call)
- Why: No shared mutable state; thread-safe tree traversal; eliminates lifetime and borrow checker friction
- Consequence: Slightly higher memory cost per render pass; simplifies mental model and reduces concurrency bugs
-
No support for animated or scripted SVG
- Why: Scope boundary: focus on static SVG correctness rather than DOM mutation or JavaScript execution
- Consequence: Cannot render dynamic SVGs or interactive content; must pre-flatten animations into single frames
-
Single pixmap output format (RGBA u8 pixels)
- Why: Simplifies the public API; covers 95% of use cases (PNG export, display, compositing)
- Consequence: No native HDR or 16-bit float output; users must convert post-render if needed
🚫Non-goals (don't propose these)
- Does not support dynamic SVG features (animations, scripting, event handlers)
- Does not provide GPU acceleration or hardware-accelerated rendering
- Does not implement real-time DOM manipulation or incremental re-rendering
- Does not handle interactive user input or event propagation
- Does not support all SVG 2.0 features (e.g., some experimental or vendor-specific extensions)
🪤Traps & gotchas
Rust MSRV: Must target Rust 1.87.0+ (stated in README); using newer std features will break older setups. Unsafe code scattered in deps: While resvg itself has minimal unsafe, font memory-mapping (mentioned in README) uses unsafe; if debugging memory issues, check dependency source. SVG spec gaps: Some SVG 1.1 features are deliberately not implemented (animations, scripting); don't assume full W3C compliance. No dynamic layout: SVG parsing happens separately from rendering, so live DOM manipulation isn't supported. C API stability: Breaking changes in crates/c-api/resvg.h require major version bump; this is a strong constraint on refactoring.
🏗️Architecture
💡Concepts to learn
- SVG Filter Primitives — Filters (blur, composite, displacement, morphology, turbulence) are complex per-pixel operations that resvg implements manually; understanding filter composition order and coordinate spaces is essential for contributing to crates/resvg/src/filter/
- Clipping and Masking (SVG) — SVG clipping paths and masks involve coordinate space conversions and rasterization; crates/resvg/src/clip.rs is a critical module where rendering correctness is tested via the 1600-test suite
- Trait-based Renderer Abstraction — resvg decouples SVG rendering from backend graphics libraries via traits; understanding the renderer trait pattern allows swapping backends (Cairo, Skia, GPU) without changing core logic
- Two-Pass SVG Processing (Parse then Render) — resvg's core design splits usvg (parsing) from rendering; this separation allows building tools like SVG transformers and validators on top of usvg without rendering, a major architectural advantage
- CSS Cascade and Specificity in SVG — SVG embeds CSS for styling; usvg resolves the cascade, and resvg must respect computed styles; misunderstanding cascade order leads to subtle rendering bugs
- Affine Transformations and Coordinate Spaces — SVG uses nested transform matrices and multiple coordinate systems (user space, viewport, etc.); crates/resvg/src/geom.rs handles these; bugs here cause misaligned or distorted output
- FFI (Foreign Function Interface) Stability — crates/c-api/lib.rs exposes Rust to C callers; breaking the ABI or struct layout is a major version bump; understanding Rust's repr attributes and cbindgen (used via cbindgen.toml) is essential for C API changes
🔗Related repos
linebender/kurbo— Curves and path handling library likely used by resvg for Bezier operations and geometric calculationslinebender/piet— 2D graphics abstraction layer; resvg's renderer traits likely align with or depend on piet's abstractionsRazrFalcon/svgparser— Alternative Rust SVG parser; useful for comparing parsing strategies and edge-case handling in the SVG specservo/rust-cssparser— CSS parser used upstream in usvg for style resolution; relevant for understanding how resvg handles CSS in SVGimage-rs/image— Rust image encoding/decoding library; resvg depends on it for PNG/JPEG output and embedded image handling
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive filter effect integration tests
The crates/resvg/src/filter/ directory contains 9 specialized filter implementations (box_blur, color_matrix, component_transfer, composite, convolve_matrix, displacement_map, iir_blur, lighting, morphology, turbulence), but the tests/extra/ directory only has 3 filter-related test cases. Each filter type deserves dedicated SVG test cases with corresponding PNG baselines to catch regressions and validate edge cases (e.g., filter region precision, transforms on filtered shapes, chained filters).
- [ ] Create tests/extra/filter-box-blur.svg and .png baseline
- [ ] Create tests/extra/filter-color-matrix.svg and .png baseline
- [ ] Create tests/extra/filter-convolve-matrix.svg and .png baseline
- [ ] Create tests/extra/filter-displacement-map.svg and .png baseline
- [ ] Create tests/extra/filter-morphology.svg and .png baseline
- [ ] Create tests/extra/filter-turbulence.svg and .png baseline
- [ ] Add test runner in tests/ to validate each SVG renders correctly against baselines
- [ ] Document test methodology in tests/README.md
Add CI workflow for WebAssembly (WASM) build verification
The repo targets multiple platforms (C API, CLI, Rust library) but .github/workflows/ lacks a WASM build check. Given SVG rendering is valuable for web use, adding a workflow to verify the library compiles to WASM and testing basic functionality would catch regressions early. This complements the existing main.yml and tagged-release.yml workflows.
- [ ] Create .github/workflows/wasm-build.yml with wasm32-unknown-unknown target
- [ ] Add wasm-bindgen dependency variant to crates/resvg/Cargo.toml if needed
- [ ] Test compilation with 'cargo build --target wasm32-unknown-unknown'
- [ ] Add basic WASM example in crates/resvg/examples/ showing JavaScript integration
- [ ] Document WASM usage in crates/resvg/README.md
Add missing path rendering edge-case tests
The crates/resvg/src/path.rs file handles SVG path rendering, but the tests/extra/ directory lacks comprehensive path test coverage (stroke linecap/linejoin, arc commands, path clipping edge cases). The existing tests focus on fill/stroke and filters but miss path-specific behaviors that are critical for correctness.
- [ ] Create tests/extra/path-arc-commands.svg and .png baseline (testing SVG arc edge cases)
- [ ] Create tests/extra/path-stroke-linecap.svg and .png baseline (butt, round, square)
- [ ] Create tests/extra/path-stroke-linejoin.svg and .png baseline (miter, round, bevel)
- [ ] Create tests/extra/path-stroke-dash-offset.svg and .png baseline (testing dasharray/dashoffset interaction)
- [ ] Create tests/extra/path-clipping.svg and .png baseline (clip-path with complex paths)
- [ ] Update tests/README.md to document path test coverage strategy
🌿Good first issues
- Add unit tests for the displacement_map filter (crates/resvg/src/filter/displacement_map.rs); currently no test coverage visible in the top-60 file list and this is a complex filter prone to off-by-one errors.
- Document the clipping algorithm in crates/resvg/src/clip.rs with inline comments; the module has no visible docstring and new contributors struggle with coordinate space conversions during clipping.
- Expand crates/resvg/examples/ with a worked example showing custom href resolver and fallback image loading (custom_href_resolver.rs exists but is minimal); many users need this pattern for embedding external assets safely.
⭐Top contributors
Click to expand
Top contributors
- @LaurenzV — 27 commits
- @RazrFalcon — 18 commits
- @waywardmonkeys — 9 commits
- @Its-Just-Nans — 7 commits
- @tomcur — 3 commits
📝Recent commits
Click to expand
Recent commits
0751011— Fixstylesheet_injectiontest not checking its 5th rect (#1046) (simartin)b3c7f58— fix: text nodes should inherit absolute transform (#1040) (fundon)961b759— Correctly calculate glyph advances (#1043) (fundon)3a0fdba— Version bump from 0.46.0 to 0.47.0 (#1016) (wmedrano)6a8ffb6— Don't exclude gitignore file from tarballs (#1017) (nicoburns)1505e4b— Add support for 2 point conical radial gradient (#1014) (wmedrano)9876cd4— Add support for variable fonts (#997) (oetiker)b8e58f5— Release v0.46.0 (#1005) (nicoburns)c48de66— Update to Rust edition 2024 (#1003) (LaurenzV)589753c— Bump dependencies, bump MSRV to 1.85 (#1002) (LaurenzV)
🔒Security observations
The resvg codebase demonstrates good foundational security practices as a Rust-based project with memory safety guarantees. However, as an SVG parser/renderer processing untrusted user input, it faces inherent risks from XXE attacks, path traversal in resource loading, and DoS via complexity attacks. The main security concerns are in the SVG processing logic (external entity handling, href resolution, resource limits) and the C API boundary. No hardcoded secrets or exposed credentials were detected. The project's dual-license approach and minimal configuration exposure are positive indicators. Recommend security audits focusing on SVG parser hardening, resource limits implementation, and C API safety guarantees.
- Medium · Potential XXE (XML External Entity) Vulnerability in SVG Processing —
crates/resvg/src/lib.rs, crates/usvg (SVG parsing components). As an SVG rendering library, resvg processes untrusted XML/SVG files. SVG parsers are known attack vectors for XXE attacks where malicious SVG files can reference external entities to read local files, cause DoS, or perform SSRF attacks. The codebase structure suggests SVG parsing is core functionality, but without reviewing the actual parsing implementation, XXE protections may be incomplete. Fix: Ensure XML/SVG parser is configured to disable external entity resolution. Use allowlists for required entity types. Add validation and limits on entity expansion. Consider using security-hardened XML parsers with XXE protection enabled by default. - Medium · Potential Path Traversal in Resource Loading —
crates/resvg/src/image.rs, crates/resvg/examples/custom_href_resolver.rs. The file structure shows examples like 'custom_href_resolver.rs' and 'image.rs', suggesting the library handles external resource references in SVGs. Malicious SVG files could exploit href resolution to access files outside intended directories via path traversal (../../../etc/passwd patterns). Fix: Implement strict path validation and canonicalization before accessing resources. Use allowlists for permitted directories. Prevent traversal sequences (../, ..\ etc). Validate resolved paths are within expected boundaries. Provide secure default href resolver. - Medium · Potential DoS via Unbounded SVG Complexity —
crates/resvg/src/filter/ (all filter implementations), crates/resvg/src/path.rs. SVG files can contain deeply nested structures, massive path data, or resource-intensive filters (blur, morphology, lighting effects visible in codebase). Malicious SVGs could cause excessive memory consumption or CPU usage, leading to denial of service. The filter implementations suggest complex processing that could be exploited. Fix: Implement resource limits: maximum nesting depth, maximum path complexity, timeout limits on filter processing. Add memory usage caps. Implement early termination on suspicious patterns. Document recommended resource limits for users. - Low · Missing SPDX License Identifier in Workspace Config —
Cargo.toml (workspace.package.license). While the project has dual licensing (Apache-2.0 OR MIT), the workspace Cargo.toml uses a non-standard format. This is more of a best-practice issue than a security vulnerability but could cause license compliance issues. Fix: Consider using standard SPDX format: 'Apache-2.0 OR MIT' is correct, but ensure all subcrates properly declare licenses. Add license-check CI/CD step. - Low · C API Potential Buffer Overflow in Custom Bindings —
crates/c-api/lib.rs, crates/c-api/resvg.h, crates/c-api/examples/cairo/. The C API (crates/c-api/) requires manual memory management and provides FFI bindings. While Rust provides memory safety, the C API boundary can be a vector for misuse. The Cairo example shows integration with external C libraries which could have buffer management issues. Fix: Review all C API functions for buffer overflow risks. Use bounds checking for array access. Provide comprehensive documentation on memory ownership and lifecycle. Add safety documentation warnings. Consider providing higher-level safe wrappers.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.