SVG is vector; PNG is raster. The hard part isn't the conversion itself — it's deciding what resolution to render at, how to handle transparent backgrounds, and what to do with unusual SVG features (filters, embedded fonts, external references). cairosvg handles 90% of cases cleanly.
Method 1: cairosvg (Cairo-based, highest quality)
cairosvg uses the Cairo graphics library — the same engine GTK and Inkscape use for SVG rendering. Output is crisp at any resolution.
pip install cairosvg
# On macOS: brew install cairo pango
# On Ubuntu: apt install libcairo2 libpango-1.0-0
import cairosvg
def svg_to_png(in_path: str, out_path: str, width: int = 2048, dpi: int = 96) -> None:
cairosvg.svg2png(
url=in_path,
write_to=out_path,
output_width=width,
dpi=dpi,
)
svg_to_png("logo.svg", "logo.png", width=2048)
# Or render at a specific output size:
svg_to_png("diagram.svg", "diagram.png", width=4096) # 4K wide
The width parameter is what determines image quality, not DPI. SVG has no inherent size — it's vector — so you choose the output resolution. Common defaults:
- width=512 — for retina app icons (256 logical, 2x for HiDPI).
- width=2048 — for high-quality web hero images.
- width=4096 — for print or 4K display.
For SVG with transparent backgrounds, the PNG inherits the transparency. To force a solid background:
cairosvg.svg2png(url="logo.svg", write_to="logo.png", background_color="white", output_width=2048)
Method 2: svglib + reportlab (pure Python, no native deps)
If you can't install Cairo (locked-down environments, certain CI runners), svglib is pure Python.
pip install svglib reportlab Pillow
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
def svg_to_png(in_path: str, out_path: str, dpi: int = 200) -> None:
drawing = svg2rlg(in_path)
renderPM.drawToFile(drawing, out_path, fmt="PNG", dpi=dpi)
svg_to_png("logo.svg", "logo.png", dpi=200)
svglib's renderer is less complete than Cairo — it handles basic SVG well (paths, text, gradients) but struggles with complex filters, masks, and certain CSS features. Use it only when you can't install Cairo.
Method 3: ChangeThisFile API (no deps, handles unusual SVGs)
For SVGs from users — different generators, custom filters, embedded fonts — the API uses the same Cairo engine but handles edge cases like font fallbacks. Get a free API key.
import requests
API_KEY = "ctf_sk_your_key_here"
def svg_to_png(in_path: str, out_path: str) -> None:
with open(in_path, "rb") as f:
response = requests.post(
"https://changethisfile.com/v1/convert",
headers={"Authorization": f"Bearer {API_KEY}"},
files={"file": f},
data={"source": "svg", "target": "png"},
timeout=30,
)
response.raise_for_status()
with open(out_path, "wb") as out:
out.write(response.content)
svg_to_png("user_logo.svg", "user_logo.png")
The API auto-detects sensible output dimensions from the SVG's viewBox and ratio. To override, pass width parameter in the form data.
When to use each
| Approach | Best for | Tradeoff |
|---|---|---|
| cairosvg | Default for production work, full SVG spec coverage | Native Cairo dep on every machine |
| svglib + reportlab | Pure-Python environments, locked-down CI | Limited SVG feature support |
| ChangeThisFile API | User-uploaded SVGs, no install hassle, edge runtimes | Per-call cost, network call |
CLI alternative: rsvg-convert or Inkscape
For one-off conversions or shell scripts:
# rsvg-convert (lightweight, ships with Cairo)
apt install librsvg2-bin
rsvg-convert -w 2048 logo.svg -o logo.png
# Inkscape (full-featured, slower startup)
inkscape -w 2048 logo.svg -o logo.png
rsvg-convert is the same engine as cairosvg's underlying library — same output quality, much faster startup than spawning Python. Use it for shell scripts; use cairosvg when conversion is part of a larger Python pipeline.
Common pitfalls
- SVG without a viewBox renders at the wrong size. If the SVG only has width/height in inches/cm, conversion can produce a tiny PNG. Add a viewBox or pass output_width explicitly.
- External fonts won't render correctly. If the SVG references a Google Font or a system font that's not installed, cairosvg falls back to a default font. Convert text to paths before exporting if you need pixel-perfect output.
- Filter effects can be slow. Gaussian blur, drop shadows, and complex masks can take seconds to render at 4K resolution. If you're batch-converting, profile a few representative SVGs first.
- The DPI parameter is misleading. SVG is resolution-independent. The DPI parameter only affects unit conversion (e.g., for fonts measured in pt). Use output_width to control actual pixel dimensions.
For production: cairosvg with width=2048 (or 4096 for print). For locked-down environments: svglib. For user uploads with unpredictable SVGs: the API. Free tier is 100 conversions/month.