¿Cómo convierto un svg
a png
en Python? Estoy almacenando el svg
en una instancia de StringIO
. ¿Debo usar la biblioteca de PyCairo? ¿Cómo escribo ese código?
¿Cómo convierto un svg
a png
en Python? Estoy almacenando el svg
en una instancia de StringIO
. ¿Debo usar la biblioteca de PyCairo? ¿Cómo escribo ese código?
Respuestas:
La respuesta es " pyrsvg ", un enlace de Python para librsvg .
Hay un paquete de Ubuntu python-rsvg que lo proporciona. Buscar en Google su nombre es pobre porque su código fuente parece estar contenido dentro del repositorio GIT del proyecto Gnome "gnome-python-desktop".
Hice un "hola mundo" minimalista que renderiza SVG en una superficie de El Cairo y lo escribe en el disco:
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")
Actualización : a partir de 2014 el paquete necesario para la distribución de Fedora Linux es: gnome-python2-rsvg
. La lista de fragmentos anterior todavía funciona como está.
cairo
determinar la ALTURA y ANCHO de la imagen por sí sola? He examinado el *.svg
archivo para extraer el ALTO y el ANCHO de allí, pero ambos están configurados en 100%
. Por supuesto, puedo analizar las propiedades de la imagen, pero como este es solo un paso en el procesamiento de la imagen, no es lo que quiero.
.get_dimension_data()
método que funcionó para mi archivo de ejemplo (un SVG con buen comportamiento) - pruébalo.
Esto es lo que hice usando 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')
¡Y funciona como un encanto!
Ver más: documento cairosvg
svg2png
tome un stream
objeto en el write_to
parámetro, y este puede ser su objeto de respuesta HTTP (que en la mayoría de los frameworks es un objeto similar a un archivo) o alguna otra secuencia, que luego sirve al navegador usando el Content-Disposition
encabezado. ver aquí: stackoverflow.com/questions/1012437/…
bytestring
acepta bytes, así que convierta la cadena primero con bytestring=bytes(svg,'UTF-8')
2). el modo de archivo debe ser binario, entoncesopen('output.png','wb')
svg2png
para mí, tenía que usarlo cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png')
.
Instale Inkscape y llámelo como línea de comando:
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}
También puede ajustar un área rectangular específica solo mediante el parámetro -j
, por ejemplo, coordinar "0: 125: 451: 217"
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}
Si desea mostrar solo un objeto en el archivo SVG, puede especificar el parámetro -i
con la identificación del objeto que ha configurado en el SVG. Oculta todo lo demás.
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
Estoy usando Wand-py (una implementación de la envoltura de Wand alrededor de ImageMagick) para importar algunos SVG bastante avanzados y hasta ahora he visto excelentes resultados. Este es todo el código que se necesita:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
Acabo de descubrir esto hoy, y sentí que valía la pena compartirlo con cualquier otra persona que pudiera encontrar esta respuesta, ya que ha pasado un tiempo desde que se respondieron la mayoría de estas preguntas.
NOTA: Técnicamente, en las pruebas, descubrí que ni siquiera tiene que pasar el parámetro de formato para ImageMagick, por lo que with wand.image.Image( blob=svg_file.read() ) as image:
era todo lo que realmente se necesitaba.
EDITAR: A partir de un intento de edición por qris, aquí hay un código útil que le permite usar ImageMagick con un SVG que tiene un fondo transparente:
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)
image.read(blob=svg_file.read(), format="svg") NameError: name 'svg_file' is not defined
svg_file
se asume que es un objeto "archivo" en este ejemplo, la configuración svg_file
se vería así:svg_file = File.open(file_name, "r")
cairo
y rsvg
'aceptado' no funcionó para mi PDF. pip install wand
y tu fragmento funcionó;)
str
entonces primero tiene que codifican en binario de la siguiente manera: svg_blob = svg_str.encode('utf-8')
. Ahora puede usar el método anterior reemplazando blob=svg_file.read()
con blob=svg_blob
.
Prueba esto: http://cairosvg.org/
El sitio dice:
CairoSVG está escrito en Python puro y solo depende de Pycairo. Se sabe que funciona en Python 2.6 y 2.7.
Actualización 25 de noviembre de 2016 :
2.0.0 es una nueva versión principal, su registro de cambios incluye:
- Soporte de Drop Python 2
<clipPath><rect ... /></clipPath>
. En segundo lugar, no acepta la opción -d (DPI).
Otra solución que acabo de encontrar aquí ¿Cómo renderizar un SVG escalado a una 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 se instala fácilmente desde un paquete binario en Windows (y lo uso para otras cosas, por lo que es fácil para mí).
Sin embargo, noté algunos problemas al convertir las banderas de países de Wikimedia, por lo que quizás no sea el analizador / renderizador svg más robusto.
Una pequeña extensión sobre la respuesta de 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)
No encontré ninguna de las respuestas satisfactoria. Todas las bibliotecas mencionadas tienen algún problema o el otro, como Cairo, que deja de admitir python 3.6 (¡dejaron de admitir Python 2 hace unos 3 años!). Además, instalar las bibliotecas mencionadas en Mac fue una molestia.
Finalmente, encontré que la mejor solución fue svglib + reportlab . ¡Ambos instalados sin problemas usando pip y la primera llamada para convertir de svg a png funcionaron maravillosamente! Muy contento con la solución.
Solo 2 comandos hacen el truco:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")
¿Hay alguna limitación que deba tener en cuenta?
Usando pycairo y librsvg pude lograr el escalado SVG y el renderizado en un mapa de bits. Suponiendo que su SVG no es exactamente de 256x256 píxeles, la salida deseada, puede leer en el SVG en un contexto de El Cairo usando rsvg y luego escalarlo y escribir en un 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')
Desde el sitio web de Cario con algunas modificaciones menores. También es un buen ejemplo de cómo llamar a una biblioteca C desde 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)
Handle.get_dimension_data
no funcionó para mí. Tuve que reemplazarlo con una simple búsqueda de self.props.width
y self.props.height
. Primero intenté definir la RsvgDimensionData
Estructura como se describe en el sitio web de El Cairo, pero sin éxito.
Aquí hay un enfoque en el que Python llama a Inkscape.
Tenga en cuenta que suprime cierta salida cruda que Inkscape escribe en la consola (específicamente, stderr y stdout) durante el funcionamiento normal sin errores. La salida se captura en dos variables de cadena out
y 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 '?') )
Por ejemplo, al ejecutar un trabajo en particular en mi sistema Mac OS, out
terminó siendo:
Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png
(El archivo svg de entrada tenía un tamaño de 339 por 339 píxeles).
En realidad, no quería depender de nada más que de Python (Cairo, Ink ..., etc.). Mis requisitos debían ser lo más simples posible, a lo sumo, un simple pip install "savior"
sería suficiente, por eso ninguno de los anteriores lo hizo ' me queda bien.
Pasé por esto (yendo más allá de Stackoverflow en la investigación). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Se ve bien hasta ahora. Así que lo comparto por si alguien se encuentra en la misma situación.