16

So I want to render SVG from python code having target resolution WxH (having SVG text as str, like this that I generate dynamically):

<svg width="200" height="200" viewBox="0 0 220 220"
     xmlns="http://www.w3.org/2000/svg">
  <filter id="displacementFilter">
    <feTurbulence type="turbulence" baseFrequency="0.05"
        numOctaves="2" result="turbulence"/>
    <feDisplacementMap in2="turbulence" in="SourceGraphic"
        scale="50" xChannelSelector="R" yChannelSelector="G"/>
  </filter>

  <circle cx="100" cy="100" r="100"
      style="filter: url(#displacementFilter)"/>
</svg>

into a png image. How to do such a thing in Python?

FlyingTeller
  • 17,638
  • 3
  • 38
  • 53
DuckQueen
  • 772
  • 10
  • 62
  • 134
  • 2
    Does this answer your question? [Convert SVG to PNG in Python](https://stackoverflow.com/questions/6589358/convert-svg-to-png-in-python) – FlyingTeller Oct 21 '21 at 12:54

2 Answers2

30

there are multiple solutions available for converting svgs to pngs in python, but not all of them will work for your particular use case since you're working with svg filters.

solution filter works? alpha channel? call directly from python?
cairosvg some* yes yes
svglib no no yes
inkscape yes yes via subprocess
wand yes yes yes

* from cairosvg documentation:

Only feOffset, feBlend and feFlood filters are supported.


note: i've added a solid white background to all the sample images to make them easier to see on a dark background, the originals did have transparent backgrounds unless stated in the table above


cairosvg

output from cairosvg

import cairosvg

# read svg file -> write png file
cairosvg.svg2png(url=input_svg_path, write_to=output_png_path, output_width=width, output_height=height)

# read svg file -> png data
png_data = cairosvg.svg2png(url=input_svg_path, output_width=width, output_height=height)

# svg string -> write png file
cairosvg.svg2png(bytestring=svg_str.encode(), write_to=output_png_path, output_width=width, output_height=height)

# svg string -> png data
png_data = cairosvg.svg2png(bytestring=svg_str.encode(), output_width=width, output_height=height)

svglib

output from svglib

from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM

# read svg -> write png
renderPM.drawToFile(svg2rlg(input_svg_path), output_png_path, fmt='PNG')

inkscape

output from inkscape

to read a file as input, put the path to the file as the last argument. to use a string as input, add the --pipe argument and pass the string to stdin.

to write to a file as output, add the argument --export-filename=+path to output file. to get the output contents directly without writing to a file, use --export-filename=- and it will be sent to stdout instead.

full documentation for CLI options here

import subprocess
inkscape = ... # path to inkscape executable

# read svg file -> write png file
subprocess.run([inkscape, '--export-type=png', f'--export-filename={output_png_path}', f'--export-width={width}', f'--export-height={height}', input_svg_path])

# read svg file -> png data
result = subprocess.run([inkscape, '--export-type=png', '--export-filename=-', f'--export-width={width}', f'--export-height={height}', input_svg_path], capture_output=True)
#   (result.stdout will have the png data)

# svg string -> write png file
subprocess.run([inkscape, '--export-type=png', f'--export-filename={output_png_path}', f'--export-width={width}', f'--export-height={height}', '--pipe'], input=svg_str.encode())

# svg string -> png data
result = subprocess.run([inkscape, '--export-type=png', '--export-filename=-', f'--export-width={width}', f'--export-height={height}', '--pipe'], input=svg_str.encode(), capture_output=True)
#   (result.stdout will have the png data)

wand

enter image description here

from wand.image import Image
from wand.Color import Color

with Color('#00000000') as bgcolor,\
  # to read input from a file:
  Image(filename=input_svg_path, width=width, height=height, background=bgcolor) as img:
  # or, to use input from a string:
  Image(blob=svg_str.encode(), format='svg', width=width, height=height, background=bgcolor) as img:
    # to save output to a file:
    with img.convert('png') as output_img:
        output_img.save(filename=output_png_path)
    # or, to get the output data in a variable:
    png_data = img.make_blob(format='png')
amgg
  • 546
  • 4
  • 8
  • 1
    Nice overview! [With my workaround from here](https://stackoverflow.com/a/74164750/1506569) you can also get `svglib` to support alpha channels (using `pymupdf`) without any external dependencies (at least on Windows/Linux/Mac) with the prebuilt wheels. – M4a1x Oct 22 '22 at 15:51
  • Side question: can ANY of these methods, apart from inkscape, properly render elements into a PNG, and does that include jpg and webp? – jokoon Jun 18 '23 at 14:38
5

you can use CairoSVG

CairoSVG is available on PyPI, you can install it with pip:

pip3 install cairosvg

in your code:

import cairosvg

width = 640
height = 480
cairosvg.svg2png(url='logo.svg', write_to='image.png', output_width=width, output_height=height)
CodeTherapy
  • 391
  • 3
  • 13