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?
-
3Possibly a duplicate of http://stackoverflow.com/questions/2932408/server-side-svg-to-png-or-some-other-image-format-in-python – Optimal Cynic Jul 05 '11 at 22:06
-
4That thread left the problem unsolved. The accepted answer came from the asker who was sharing his failed code attempt. The other answer suggested ImageMagick but a commenter said ImageMagick does "a horrible job of interpreting SVG." I don't want my pngs to look horrible so I'm re-asking the question. – ram1 Jul 05 '11 at 22:21
-
1Try http://cairographics.org/cookbook/librsvgpython/ – Optimal Cynic Jul 05 '11 at 22:26
-
The examples in that link are specific to Win32. I'm running linux. – ram1 Jul 05 '11 at 22:49
-
Take a look at [this](http://guillaume.segu.in/blog/code/43/svg-to-png/) blog post, it looks like it might be what you need. – giodamelio Jul 06 '11 at 09:24
-
Simple SVGs: https://github.com/aslpavel/svgrasterize.py – doublemax Mar 06 '21 at 19:48
14 Answers
Here is what I did using cairosvg:
from cairosvg import svg2png
svg_code = """
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12" y2="16"/>
</svg>
"""
svg2png(bytestring=svg_code,write_to='output.png')
And it works like a charm!
See more: cairosvg document
-
8Hi. Do you know how can i do the same but without writing to a file? I need to push png content to the browser from a webserver, so that way the user can download the image. Saving the png file is not a valid option in our project, that's why I need it that way. Thanks – estemendoza Feb 25 '13 at 18:11
-
5I've been doing this myself. It basically depends on what tools or frameworks u have at hand when handling your web requests, but no matter what it is, the basic idea is that `svg2png` takes in a `stream` object in the `write_to` parameter, and this can either be your HTTP Response object (which in most frameworks is a file-like object) or some other stream, which you then serve to the browser using the `Content-Disposition` header. see here: http://stackoverflow.com/questions/1012437/uses-of-content-disposition-in-an-http-response-header – JWL Feb 26 '13 at 06:59
-
4For those who experiences the issues with that code, as I was: 1). `bytestring` accepts bytes, so convert string first with `bytestring=bytes(svg,'UTF-8')` 2). file mode should be binary, so `open('output.png','wb')` – Serj Zaharchenko Oct 19 '14 at 13:28
-
The equivalent of @SerjZaharchenko's answer for Python 2.x is `bytestring=svg.encode('utf-8')` – supervacuo Feb 26 '15 at 14:55
-
If you want to set the width and height with `cairosvg`, the SVG must have a [`viewbox`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox). You can add one using [`scour`](https://github.com/scour-project/scour#usage). – z0r Aug 12 '16 at 00:11
-
3cairosvg supports only Python 3.4+. They have dropped Python 2 support – Marlon Abeykoon Sep 09 '16 at 10:32
-
Note that for python 2.7 you need to `pip install cairosvg==1.0.22` , otherwise you'll have installation issues. – Amir Rosenfeld Aug 22 '17 at 14:01
-
1There wasn't a `svg2png` for me, I had to use `cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png')`. – tobltobs Sep 12 '18 at 08:09
-
1
-
1@estemendoza Yes; here's how to do that: `PIL.Image.open(io.BytesIO(cairosvg.svg2png(url=filename, write_to=None)))` – JamesTheAwesomeDude May 23 '21 at 20:37
The answer is "pyrsvg" - a Python binding for librsvg.
There is an Ubuntu python-rsvg package providing it. Searching Google for its name is poor because its source code seems to be contained inside the "gnome-python-desktop" Gnome project GIT repository.
I made a minimalist "hello world" that renders SVG to a cairo surface and writes it to disk:
import cairo
import rsvg
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 640,480)
ctx = cairo.Context(img)
## handle = rsvg.Handle(<svg filename>)
# or, for in memory SVG data:
handle= rsvg.Handle(None, str(<svg data>))
handle.render_cairo(ctx)
img.write_to_png("svg.png")
Update: as of 2014 the needed package for Fedora Linux distribution is: gnome-python2-rsvg. The above snippet listing still works as-is.
- 86,446
- 9
- 131
- 182
-
1Great, works nicely. But is there a way to let `cairo` determine the HEIGHT and WIDTH of the picture on its own? I've looked into the `*.svg` file, to extract the HEIGHT and WIDTH from there, but it is both set to `100%`. Of course, I can look into the properties of the picture, but since this is only one step in image processing this is not what I want. – quapka Jun 09 '14 at 09:19
-
1If the "width" and "height" of your files are set to 100%, there is no magic Cairo or rsvg can do to guess the size: such SVG files were left size independent by the creator software(/person). The surrounding HTML code to import the SVG file would supply the physical size. However, the "Handle" object of rsvg do have a `.get_dimension_data()` method that worked for my example file (a well behaved SVG) - give it a try. – jsbueno Jun 12 '14 at 05:50
-
1
-
1Is there a quick command for adding a white background to the image if its current background is transparent? – fiatjaf Jan 07 '15 at 03:23
-
@jsbueno I use Windows 8.1 and python 2.7.11 How can I install cairo and rsvg and make it work. I was struggling to make this work. BTW +1 for your detailed explanation. – Marlon Abeykoon Sep 09 '16 at 10:24
-
@MarlonAbeykoon: I think it is better you ask this as a separate question on another Stack Exchange forum - (I actually have no idea). Likely http://superuser.com is your best choice. Just put a link to this question on your question there to give some more context to whoever knows how to answer this. – jsbueno Sep 09 '16 at 13:50
-
I used bit old version of wand to do it. Since latest version had some issues – Marlon Abeykoon Sep 10 '16 at 10:33
-
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}
-
3+1 because this is also extremely handy for shell scripting. See http://inkscape.org/doc/inkscape-man.html for full docs on Inkscape's command line. – Prime Jul 06 '13 at 23:24
-
Thanks, this was the easiest way I found to do this. On Windows, to make it so that you don't have to type in the full path to Inkscape every time, you can [add it to your Path in Environmental Variables](http://www.computerhope.com/issues/ch000549.htm). – Alex S Nov 12 '16 at 00:40
-
6This is not an answer. It's a workaround. OP asked for a Python solution. – lamino Oct 29 '18 at 23:02
I'm using Wand-py (an implementation of the Wand wrapper around ImageMagick) to import some pretty advanced SVGs and so far have seen great results! This is all the code it takes:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
I just discovered this today, and felt like it was worth sharing for anyone else who might straggle across this answer as it's been a while since most of these questions were answered.
NOTE: Technically in testing I discovered you don't even actually have to pass in the format parameter for ImageMagick, so with wand.image.Image( blob=svg_file.read() ) as image: was all that was really needed.
EDIT: From an attempted edit by qris, here's some helpful code that lets you use ImageMagick with an SVG that has a transparent background:
from wand.api import library
import wand.color
import wand.image
with wand.image.Image() as image:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(image.wand,
background_color.resource)
image.read(blob=svg_file.read(), format="svg")
png_image = image.make_blob("png32")
with open(output_filename, "wb") as out:
out.write(png_image)
- 4,531
- 31
- 30
-
3
-
Thanks @qris - added your transparent background SVG rendering code to my answer as well. – streetlogics May 12 '14 at 14:40
-
-
4I get the error `image.read(blob=svg_file.read(), format="svg") NameError: name 'svg_file' is not defined` – adrienlucca.net Jul 15 '16 at 11:56
-
4`svg_file` is assumed to be a "file" object in this example, setting `svg_file` would look something like: `svg_file = File.open(file_name, "r")` – streetlogics Jul 15 '16 at 18:04
-
2Thanks, the `cairo` and `rsvg` 'accepted' method didn't work for my PDF. `pip install wand` and your snippet did the trick ;) – nmz787 Dec 26 '16 at 06:27
-
6If you have an svg `str` then you first need to encode into binary like this: `svg_blob = svg_str.encode('utf-8')`. Now you can use the method above by replacing `blob=svg_file.read()` with `blob=svg_blob`. – asherbret Sep 30 '18 at 10:18
-
This still didn't export correctly with fonts... I know that's not helpful... so specifically, I have an svg that utilizes the font-family property and loads all of the fonts from external stylesheets generated by font squirrel . com ... – Shmack Oct 25 '20 at 17:13
-
FYI, i had trouble directly running the above code. First thing is that i had to install `potrace` from bioconda. Second, i actually end up using this `svg_to_png` function from https://www.programcreek.com/python/?code=yaqwsx%2FPcbDraw%2FPcbDraw-master%2Fpcbdraw%2Fpcbdraw.py# -- i hope it will be helpful for someone else too – Forrest1988 Jun 02 '21 at 16:16
-
With Wand 0.6.6 in MacOs, png_image was always None, make it works by adding background in constructor too: `image.read(blob=svg_file.read(), background=background_color), format="svg")` – Marius Jun 22 '21 at 09:34
-
FYI, this solution requires both Imagemagick and Inkscape to be installed. – Brōtsyorfuzthrāx Oct 14 '21 at 03:14
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?
-
Yes, marker-end is not supported, and won't be, https://github.com/deeplook/svglib/issues/177 – Rainald62 Apr 16 '20 at 16:35
-
5Great solution for those that work on Windows, absolutely no dependencies required at SO level. Thanks!! – luismesas Feb 20 '21 at 18:08
-
In case you need to continue some operations with PIL `pil_img = renderPM.drawToPILP(drawing)` – Rami Alloush Mar 06 '21 at 00:05
-
Looks great, but svglib doesn't install properly via pip on Termux (the above answers don't seem to work on Termux, either, though). – Brōtsyorfuzthrāx Oct 14 '21 at 03:23
Try this: http://cairosvg.org/
The site says:
CairoSVG is written in pure python and only depends on Pycairo. It is known to work on Python 2.6 and 2.7.
Update November 25, 2016:
2.0.0 is a new major version, its changelog includes:
- Drop Python 2 support
- 12,968
- 10
- 84
- 107
- 237
- 1
- 6
-
There is two problem with this, unfortunately. First, it doesn't handle the `
`. Second, it doesn't take the -d (DPI) option. – Ray Nov 20 '12 at 09:58 -
2@Ray , please send bug reports / feature requests on [the CairoSVG tracker](https://github.com/Kozea/CairoSVG/issues)! – Simon Sapin Jan 22 '13 at 12:49
-
@Simon, Can you do it please? I'm too busy and I will be in the next 1-2 month. – Ray Jan 22 '13 at 16:35
-
2@Ray, actually the -d / --dpi option has been there for a while now, and I’m told that support for
was added a few weeks back in the git version. – Simon Sapin Jan 22 '13 at 22:13 -
-
I had some problems with SVGs that use opacity. In my case Wand-py showed to be a better solution. – ruhanbidart May 08 '15 at 02:35
Another solution I've just found here How to render a scaled SVG to a QImage?
from PySide.QtSvg import *
from PySide.QtGui import *
def convertSvgToPng(svgFilepath,pngFilepath,width):
r=QSvgRenderer(svgFilepath)
height=r.defaultSize().height()*width/r.defaultSize().width()
i=QImage(width,height,QImage.Format_ARGB32)
p=QPainter(i)
r.render(p)
i.save(pngFilepath)
p.end()
PySide is easily installed from a binary package in Windows (and I use it for other things so is easy for me).
However, I noticed a few problems when converting country flags from Wikimedia, so perhaps not the most robust svg parser/renderer.
A little extension on the answer of jsbueno:
#!/usr/bin/env python
import cairo
import rsvg
from xml.dom import minidom
def convert_svg_to_png(svg_file, output_file):
# Get the svg files content
with open(svg_file) as f:
svg_data = f.read()
# Get the width / height inside of the SVG
doc = minidom.parse(svg_file)
width = int([path.getAttribute('width') for path
in doc.getElementsByTagName('svg')][0])
height = int([path.getAttribute('height') for path
in doc.getElementsByTagName('svg')][0])
doc.unlink()
# create the png
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(img)
handler = rsvg.Handle(None, str(svg_data))
handler.render_cairo(ctx)
img.write_to_png(output_file)
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="svg_file",
help="SVG input file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output", default="svg.png",
help="PNG output file", metavar="FILE")
args = parser.parse_args()
convert_svg_to_png(args.svg_file, args.output)
- 108,021
- 142
- 552
- 849
-
I used the svg width and height extraction. I'm not sure about the svg standard but in some of my svg files the width or height were followed by a non numeric string such as 'mm' or 'px' (ex: '250mm'). The int('250mm') throws an exception and I had to make some additional tweaks. – WigglyWorld Feb 15 '15 at 11:12
Here is a another solution without using rsvg(which is currently not available for windows).Only install cairosvg using pip install CairoSVG
svg2png.py
from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')
- 51
- 1
- 4
-
2Another simpler way: `cairosvg.svg2png(url="/path/to/input.svg", write_to="/tmp/output.png")`. As shared in Cairosvg's official documentation: https://cairosvg.org/documentation/ – Amit Dash Jun 04 '21 at 07:05
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.)
- 2,082
- 4
- 17
- 29
-
-
2@Joel: First line has been modified to overcome your objection. But of course, even a "pure" Python solution relies on elements outside the core language, and is ultimately run with machine language, so perhaps there is no such thing as end-to-end anything! – Iron Pillow Aug 01 '19 at 17:54
SVG scaling and PNG rendering
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.
main.py
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')
RSVG C binding
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)
- 20,467
- 6
- 114
- 123
-
Thanks for this, it proved very useful in a project of mine. Although `Handle.get_dimension_data` didn't work for me. I had to replace it with a simple fetching of `self.props.width` and `self.props.height`. I first tried defining the `RsvgDimensionData` Structure as described on the cairo website, but without success. – JeanOlivier Oct 01 '18 at 19:26
-
I am trying to use this in a project of mine. How do I obtain the dll files required? – Andoo Jun 11 '19 at 13:33
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')
- 2,368
- 25
- 27
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])
- 11
- 1
-
I am sure it would help the community if you explained to us why and how you code would solve the OP's problem – Simas Joneliunas Jan 12 '22 at 11:23
-
This works with SVG files that are not correctly rendered by Cairo, Inkspace but are correctly rendered by Gimp and Image Viewer. – FaST4 Feb 14 '22 at 20:09
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.
- 2,156
- 1
- 22
- 26
-
1can you please include the relevant part of that article to your answer ? – Ciprian Tomoiagă Sep 18 '20 at 06:55
-
looking at it, it seems to be identical to [Sarang's answer](https://stackoverflow.com/a/59505217/786559) – Ciprian Tomoiagă Sep 18 '20 at 06:57