# Svgager
High-performance SVG to image conversion library for Elixir, powered by Rust via Rustler.
## Features
- **Multiple Output Formats**: Convert SVG to PNG, JPG, JPEG, GIF, or WebP
- **Resolution Control**: Set output width, height, or both dimensions
- **Aspect Ratio Preservation**: Automatically maintains aspect ratio when only one dimension is specified
- **Transparent Backgrounds**: PNG format supports transparency by default
- **Configurable Backgrounds**: Other formats support custom background colors (hex format)
- **SVG Preprocessing**: Replace strings in SVG content before conversion (useful for dynamic color changes)
- **High Performance**: Built with Rust for maximum speed and efficiency
## Prerequisites
### For End Users (Installing as Dependency)
**Elixir** 1.19 or later
**Note**: Rust is **NOT required** for end users! Svgager uses precompiled NIFs that work out of the box on supported platforms (Linux, macOS, Windows).
### For Developers (Contributing/Testing)
**Elixir** 1.19 or later
**Rust** and **Cargo** - **REQUIRED** for running tests and development
> **Quick Summary**:
> - **End users** (adding to your app): No Rust needed
> - **Contributors** (running `mix test`): Rust required
### Supported Platforms (Precompiled Binaries)
Precompiled binaries are provided for:
- Linux (x86_64, aarch64)
- macOS (x86_64, aarch64/Apple Silicon)
- Windows (x86_64)
### Building from Source (Optional)
If you need to compile from source, you'll need Rust and Cargo:
```bash
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Restart terminal or run:
source $HOME/.cargo/env
# Verify installation
cargo --version
rustc --version
# Force compilation from source
export SVGAGER_BUILD=true
mix deps.compile svgager
```
Or visit [https://rustup.rs/](https://rustup.rs/) for other installation methods.
## Installation
Add `svgager` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:svgager, "~> 0.1.0"}
]
end
```
Then run:
```bash
mix deps.get
mix compile
```
### Precompiled Binaries
Svgager uses `rustler_precompiled` to provide precompiled NIFs for common platforms. When you run `mix deps.get`, it will automatically download the appropriate precompiled binary for your platform from GitHub releases.
**Environment Variables:**
- `SVGAGER_BUILD=true` - Force compilation from source instead of using precompiled binaries
- `RUSTLER_PRECOMPILED_FORCE_BUILD=true` - Alternative way to force building from source
**For Package Maintainers:**
To release precompiled binaries:
```bash
# 1. Update version in mix.exs
# 2. Update the base_url in lib/svgager/native.ex with your GitHub repository
# 3. Run the release helper
mix rustler_precompiled.download Svgager.Native --all --print
# 4. Upload the generated .tar.gz files to GitHub releases
# 5. Generate checksums
mix rustler_precompiled.download Svgager.Native --all --print-checksum > checksum-Elixir.Svgager.Native.exs
# 6. Commit the checksum file
```
## Usage
### Basic Conversion
```elixir
# Read an SVG file
svg_content = File.read!("input.svg")
# Convert to PNG with transparency (800px width, height auto-calculated)
{:ok, png_data} = Svgager.convert(svg_content, format: :png, width: 800)
File.write!("output.png", png_data)
```
### JPG with Background Color
```elixir
# Convert to JPG with red background
{:ok, jpg_data} = Svgager.convert(svg_content,
format: :jpg,
width: 1200,
height: 800,
background_color: "FF0000"
)
File.write!("output.jpg", jpg_data)
```
### WebP with Custom Resolution
```elixir
# Convert to WebP maintaining aspect ratio
{:ok, webp_data} = Svgager.convert(svg_content,
format: :webp,
width: 1024,
background_color: "FFFFFF"
)
File.write!("output.webp", webp_data)
```
### SVG Preprocessing
You can replace strings in the SVG before conversion, useful for changing colors dynamically:
```elixir
# Change colors before conversion
{:ok, png_data} = Svgager.convert(svg_content,
format: :png,
width: 800,
replacements: %{
"#000000" => "#FF5500",
"#FFFFFF" => "#00AAFF",
"blue" => "red"
}
)
```
### GIF Format
```elixir
# Convert to GIF with white background
{:ok, gif_data} = Svgager.convert(svg_content,
format: :gif,
width: 600,
height: 400,
background_color: "FFFFFF"
)
File.write!("output.gif", gif_data)
```
## API Reference
### `Svgager.convert/2`
Converts SVG to the specified image format and returns binary data.
#### Parameters
- `svg_string` (String.t) - The SVG content as a string
- `opts` (Keyword.t) - Conversion options
#### Options
- `:format` (required) - Output format. One of `:png`, `:jpg`, `:jpeg`, `:gif`, or `:webp`
- `:width` (optional) - Output width in pixels (integer). If only width is provided, height is calculated to maintain aspect ratio
- `:height` (optional) - Output height in pixels (integer). If only height is provided, width is calculated to maintain aspect ratio
- `:background_color` (optional) - Background color as hex string (e.g., "FFFFFF" or "#FF0000"). Ignored for PNG format which uses transparency. Defaults to "FFFFFF" (white) for other formats
- `:replacements` (optional) - Map of string replacements to apply to SVG before conversion (e.g., `%{"#000000" => "#FF0000"}`)
When both `:width` and `:height` are provided, the output uses exact dimensions (may distort if aspect ratio doesn't match original SVG).
#### Returns
- `{:ok, binary_data}` - Binary image data on success
- `{:error, reason}` - Error string on failure
## Error Handling
```elixir
case Svgager.convert(svg_content, format: :png, width: 800) do
{:ok, image_data} ->
File.write!("output.png", image_data)
IO.puts("Conversion successful!")
{:error, reason} ->
IO.puts("Conversion failed: #{reason}")
end
```
## Development
### Prerequisites for Development
**Important**: Rust and Cargo are **REQUIRED** for development and testing!
While end users don't need Rust (they use precompiled binaries), developers need Rust to:
- Run tests locally
- Make changes to the Rust code
- Build the library from source
**Install Rust:**
```bash
# Option 1: Using rustup (recommended)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
# Option 2: Using mise (if you use mise for version management)
mise install rust@latest
mise use rust@latest
# Verify installation
cargo --version
rustc --version
```
### Setting Up Development Environment
The library **automatically builds from source** in dev/test mode (configured via `config/dev.exs` and `config/test.exs`):
```bash
# Clone the repository
git clone <repository-url>
cd svgager
# Install dependencies
mix deps.get
# Compile (automatically builds Rust NIF from source in dev mode)
mix compile
# Run tests
mix test
```
**Build Configuration:**
The build behavior is controlled by Mix configs:
- **`config/dev.exs`**: Sets `force_build_nif: true` (builds from source)
- **`config/test.exs`**: Sets `force_build_nif: true` (builds from source)
- **`config/prod.exs`**: Sets `force_build_nif: false` (uses precompiled binaries)
You can override this with the `SVGAGER_BUILD` environment variable:
```bash
# Force building from source in any environment
SVGAGER_BUILD=true mix compile
# Or export it for your session
export SVGAGER_BUILD=true
mix compile
```
**Development Tips:**
- The Rust NIF compiles in debug mode by default (faster compilation)
- Use `MIX_ENV=prod mix compile` to compile in release mode (optimized, slower compilation)
- The first compilation will take a few minutes (Rust dependencies are cached after that)
- Subsequent recompiles are much faster
### Project Structure
```
svgager/
├── lib/
│ ├── svgager.ex # Main public API
│ └── svgager/
│ ├── converter.ex # High-level conversion logic
│ └── native.ex # NIF module definition
├── native/
│ └── svgager_native/
│ ├── Cargo.toml # Rust dependencies
│ └── src/
│ ├── lib.rs # Rust NIF entry point
│ └── converter.rs # Rust conversion implementation
├── test/
│ └── svgager_test.exs
└── mix.exs
```
## Technical Details
### Rust Dependencies
- **rustler** - Elixir NIF bindings
- **resvg** - High-quality SVG rendering
- **usvg** - SVG parsing and tree representation
- **tiny-skia** - 2D rendering backend
- **image** - Multi-format image encoding
## Performance
Svgager leverages Rust's performance for fast SVG rendering and image encoding. The library uses resvg, which is widely regarded as one of the highest-quality SVG renderers available.
## Contributing
We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on:
- Setting up your development environment
- Coding standards and style guides
- Testing requirements
- Pull request process
- Reporting issues
### Quick Start for Contributors
```bash
# Fork and clone the repository
git clone https://github.com/OutdoorMap/svgager.git
cd svgager
# Install dependencies
mix deps.get
# Compile (requires Rust)
mise exec -- mix compile
# Run tests
mise exec -- mix test
# Format code before committing
mix format
cd native/svgager_native && cargo fmt
```
### Ways to Contribute
- Report bugs and issues
- Suggest new features or improvements
- Improve documentation
- Submit pull requests
- Star the project on GitHub
## License
Copyright (c) 2026 OutdoorMap AB
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
### What This Means
**You can:**
- Use this library in commercial projects
- Modify and distribute the code
- Use it privately
- Sublicense it
**You must:**
- Include the license and copyright notice
- Acknowledge the original authors
**You cannot:**
- Hold the authors liable for damages
- Claim warranty coverage
## Acknowledgments
- Built with [Rustler](https://github.com/rusterlium/rustler) for Elixir/Rust integration
- Uses [resvg](https://github.com/RazrFalcon/resvg) for high-quality SVG rendering
- Powered by [tiny-skia](https://github.com/RazrFalcon/tiny-skia) for 2D graphics
- Image encoding via [image-rs](https://github.com/image-rs/image)
## Support
- **Documentation**: See [README.md](README.md) and [CONTRIBUTING.md](CONTRIBUTING.md)
- **Issues**: [GitHub Issues](https://github.com/OutdoorMap/svgager/issues)
- **Discussions**: [GitHub Discussions](https://github.com/OutdoorMap/svgager/discussions)
## Project Status
- **Production Ready**: All core features implemented and tested
- **Well Tested**: 106 tests covering all functionality
- **Documented**: Comprehensive documentation and examples
- **Precompiled Binaries**: Coming soon (requires GitHub releases setup)
## Roadmap
Potential future enhancements:
- [ ] Additional image formats (AVIF, TIFF, BMP)
- [ ] SVG animation support
- [ ] Image optimization options
- [ ] Batch conversion utilities
- [ ] CLI tool for command-line usage
- [ ] More preprocessing options (filters, effects)
Suggestions welcome! Open an issue to discuss new features.