How do I convert an svg to png, in Python? I am storing the svg in an instance of StringIO. Should I use the pyCairo library? How do I write that code?
How do I convert an svg to png, in Python? I am storing the svg in an instance of StringIO. Should I use the pyCairo library? How do I write that code?
Install Inkscape and call it as command line:
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}
You can also snap specific rectangular area only using parameter -j, e.g. co-ordinate "0:125:451:217"
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}
If you want to show only one object in the SVG file, you can specify the parameter -i with the object id that you have setup in the SVG. It hides everything else.
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
I did not find any of the answers satisfactory. All the mentioned libraries have some problem or the other like Cairo dropping support for python 3.6 (they dropped Python 2 support some 3 years ago!). Also, installing the mentioned libraries on the Mac was a pain.
Finally, I found the best solution was svglib + reportlab. Both installed without a hitch using pip and first call to convert from svg to png worked beautifully! Very happy with the solution.
Just 2 commands do the trick:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")
Are there any limitations with these I should be aware of?
Here is a another solution without using rsvg(which is currently not available for windows).Only install cairosvg using pip install CairoSVG
from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')
Here is an approach where Inkscape is called by Python.
Note that it suppresses certain crufty output that Inkscape writes to the console (specifically, stderr and stdout) during normal error-free operation. The output is captured in two string variables, out and err.
import subprocess # May want to use subprocess32 instead
cmd_list = [ '/full/path/to/inkscape', '-z',
'--export-png', '/path/to/output.png',
'--export-width', 100,
'--export-height', 100,
'/path/to/input.svg' ]
# Invoke the command. Divert output that normally goes to stdout or stderr.
p = subprocess.Popen( cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
# Below, < out > and < err > are strings or < None >, derived from stdout and stderr.
out, err = p.communicate() # Waits for process to terminate
# Maybe do something with stdout output that is in < out >
# Maybe do something with stderr output that is in < err >
if p.returncode:
raise Exception( 'Inkscape error: ' + (err or '?') )
For example, when running a particular job on my Mac OS system, out ended up being:
Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png
(The input svg file had a size of 339 by 339 pixels.)
Using pycairo and librsvg I was able to achieve SVG scaling and rendering to a bitmap. Assuming your SVG is not exactly 256x256 pixels, the desired output, you can read in the SVG to a Cairo context using rsvg and then scale it and write to a PNG.
import cairo
import rsvg
width = 256
height = 256
svg = rsvg.Handle('cool.svg')
unscaled_width = svg.props.width
unscaled_height = svg.props.height
svg_surface = cairo.SVGSurface(None, width, height)
svg_context = cairo.Context(svg_surface)
svg_context.save()
svg_context.scale(width/unscaled_width, height/unscaled_height)
svg.render_cairo(svg_context)
svg_context.restore()
svg_surface.write_to_png('cool.png')
From the Cario website with some minor modification. Also a good example of how to call a C-library from Python
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path.encode(), byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def get_dimension_data(self):
svgDim = self.RsvgDimensionData()
_librsvg.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
return (svgDim.width, svgDim.height)
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
Try this python script:
Don't forget to install cairosvg: pip3 install cairosvg
#!/usr/bin/env python3
import os
import cairosvg
for file in os.listdir('.'):
if os.path.isfile(file) and file.endswith(".svg"):
name = file.split('.svg')[0]
cairosvg.svg2png(url=name+'.svg',write_to=name+'.png')
Try using Gtk.Image and Gdk.Pixbuf
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk, Gtk
from PIL import Image
image = Gtk.Image()
image.set_from_file("path/to/image.svg")
pb = image.get_pixbuf()
pb.savev("path/to/convented/image.jpeg","jpeg",[],[])
im = Image.open("path/to/convented/image.jpeg")
pix = im.load()
print(pix[1,1])
Actually, I did not want to be dependent of anything else but Python (Cairo, Ink.., etc.)
My requirements were to be as simple as possible, at most, a simple pip install "savior" would suffice, that's why any of those above didn't suit for me.
I came through this (going further than Stackoverflow on the research). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Looks good, so far. So I share it in case anyone in the same situation.
All the answer's here are great, but I figure I'll mention that I have made a simple library that loads SVG's files as pillow Image instances which can then be exported. It uses inkscape like in blj's answer, but renders to stdout so that no temporary files are made. There's some basic usage stuff in the README.
https://github.com/jlwoolf/pillow-svg
EDIT:
As suggested, here's a brief explanation, since the link could become invalid:
The library uses inkscape's command line interface to convert the image to a png of a specific size or dpi using the python subprocess library. By setting --export-filename to -, inkscape redirects the output to the stdout. The first two lines are discarded, and the remaining output is passed to PIL.Image.open, converting it to pillow image instance.
import subprocess
from PIL import Image
options = ["inkscape", "--export-filename=-", "--export-type=png", "file.svg"]
pipe = subprocess.Popen(options, stdout=subprocess.PIPE)
pipe.stdout.readline()
pipe.stdout.readline()
img = Image.open(pipe.stdout)
From there you can do whatever pillow image operations you need (like export as a jpg, resize, crop, etc).
EDIT 2:
Just added support for skia-python (haven't fully tested it, but seems to work so far). This way you can convert an svg to png with only a single pip install (no need to use inkscape).
Here is an explanation of how the library uses skia-python:
First, the svg file is loaded into a skia.SVGDOM. From there you can grab the SVGDOM's dimensions, using containerSize. Then a skia.Surface of the desired image output size is made. The canvas is scaled to fit the svg to the surface, and then the svg is rendered. From there, an image snapshot can be made, which can then be fed to PIL.Image.open.
import skia
from PIL import Image
skia_stream = skia.Stream.MakeFromFile("file.svg")
skia_svg = skia.SVGDOM.MakeFromStream(skia_stream)
svg_width, svg_height = skia_svg.containerSize()
surface_width, surface_height = 512, 512
surface = skia.Surface(surface_width, surface_height)
with surface as canvas:
canvas.scale(surface_width / svg_width, surface_height / svg_height)
skia_svg.render(canvas)
with io.BytesIO(surface.makeImageSnapshot().encodeToData()) as f:
img = Image.open(f)
img.load()