Come posso convertire un svg
in png
, in Python? Sto memorizzando svg
in un'istanza di StringIO
. Devo usare la libreria pyCairo? Come scrivo quel codice?
Come posso convertire un svg
in png
, in Python? Sto memorizzando svg
in un'istanza di StringIO
. Devo usare la libreria pyCairo? Come scrivo quel codice?
Risposte:
La risposta è " pyrsvg " - un'associazione Python per librsvg .
C'è un pacchetto Ubuntu python-rsvg che lo fornisce. La ricerca su Google per il suo nome è scarsa perché il suo codice sorgente sembra essere contenuto all'interno del repository Gnome del progetto Gnome "gnome-python-desktop".
Ho creato un "ciao mondo" minimalista che rende SVG una superficie del cairo e lo scrive su 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")
Aggiornamento : a partire dal 2014 il pacchetto necessario per la distribuzione Fedora Linux è: gnome-python2-rsvg
. L'elenco di frammenti di cui sopra funziona ancora così com'è.
cairo
determinare da solo l'ALTEZZA e la LARGHEZZA dell'immagine? Ho esaminato il *.svg
file per estrarre l'ALTEZZA e la LARGHEZZA da lì, ma sono entrambe impostate su 100%
. Certo, posso esaminare le proprietà dell'immagine, ma poiché questo è solo un passaggio nell'elaborazione dell'immagine, non è quello che voglio.
.get_dimension_data()
metodo che ha funzionato per il mio file di esempio (un SVG ben comportato) - provalo.
Ecco cosa ho fatto 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')
E funziona come un incantesimo!
Vedi di più: documento cairosvg
svg2png
accetta un stream
oggetto nel write_to
parametro, e questo può essere il tuo oggetto Risposta HTTP (che in la maggior parte dei framework è un oggetto simile a un file) o qualche altro flusso, che poi servi al browser usando l' Content-Disposition
intestazione. vedere qui: stackoverflow.com/questions/1012437/...
bytestring
accetta byte, quindi converti prima la stringa con bytestring=bytes(svg,'UTF-8')
2). la modalità file dovrebbe essere binaria, quindiopen('output.png','wb')
svg2png
per me, dovevo usare cairosvg.surface.PNGSurface.convert(svg_str, write_to='output.png')
.
Installa Inkscape e chiamalo come riga di comando:
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}
Puoi anche catturare un'area rettangolare specifica utilizzando solo il parametro -j
, ad es. Coordinate "0: 125: 451: 217"
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}
Se vuoi mostrare un solo oggetto nel file SVG, puoi specificare il parametro -i
con l'id oggetto che hai impostato nell'SVG. Nasconde tutto il resto.
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
Sto usando Wand-py (un'implementazione del wrapper Wand attorno a ImageMagick) per importare alcuni SVG piuttosto avanzati e finora ho visto ottimi risultati! Questo è tutto il codice che serve:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
L'ho scoperto solo oggi e ho sentito come se valesse la pena condividerlo con chiunque altro potesse trovare questa risposta perché è passato un po 'di tempo da quando la maggior parte di queste domande ha avuto risposta.
NOTA: tecnicamente durante i test ho scoperto che non devi nemmeno passare il parametro di formato per ImageMagick, quindi with wand.image.Image( blob=svg_file.read() ) as image:
era tutto ciò che era veramente necessario.
EDIT: da un tentativo di modifica da parte di qris, ecco un codice utile che ti consente di utilizzare ImageMagick con un SVG che ha uno sfondo trasparente:
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
si presume che sia un oggetto "file" in questo esempio, l'impostazione svg_file
sarebbe simile a:svg_file = File.open(file_name, "r")
cairo
e rsvg
"accettato" non ha funzionato per il mio PDF. pip install wand
e il tuo snippet ha funzionato;)
str
allora necessario prima di codificare in binario in questo modo: svg_blob = svg_str.encode('utf-8')
. Ora puoi usare il metodo sopra sostituendo blob=svg_file.read()
con blob=svg_blob
.
Prova questo: http://cairosvg.org/
Il sito dice:
CairoSVG è scritto in puro python e dipende solo da Pycairo. È noto per funzionare su Python 2.6 e 2.7.
Aggiornamento del 25 novembre 2016 :
2.0.0 è una nuova versione principale, il suo log delle modifiche include:
- Elimina il supporto per Python 2
<clipPath><rect ... /></clipPath>
. Secondo, non accetta l'opzione -d (DPI).
Un'altra soluzione che ho appena trovato qui Come rendere un SVG ridimensionato in 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 si installa facilmente da un pacchetto binario in Windows (e lo uso per altre cose, quindi è facile per me).
Tuttavia, ho notato alcuni problemi durante la conversione delle bandiere dei paesi da Wikimedia, quindi forse non è il parser / renderer svg più robusto.
Una piccola estensione sulla risposta di 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)
Non ho trovato soddisfacente nessuna delle risposte. Tutte le librerie menzionate hanno qualche problema o l'altro come Cairo ha abbandonato il supporto per python 3.6 (hanno abbandonato il supporto per Python 2 circa 3 anni fa!). Inoltre, l'installazione delle librerie menzionate sul Mac è stata una seccatura.
Infine, ho trovato che la soluzione migliore era svglib + reportlab . Entrambi installati senza intoppi utilizzando pip e la prima chiamata per convertire da svg a png hanno funzionato perfettamente! Molto soddisfatto della soluzione.
Solo 2 comandi fanno il trucco:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")
Ci sono limitazioni con questi di cui dovrei essere a conoscenza?
Utilizzando pycairo e librsvg sono stato in grado di ottenere il ridimensionamento SVG e il rendering in una bitmap. Supponendo che il tuo SVG non sia esattamente 256x256 pixel, l'output desiderato, puoi leggere nell'SVG in un contesto Cairo usando rsvg, quindi ridimensionarlo e scrivere in 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')
Dal sito Cario con qualche piccola modifica. Anche un buon esempio di come chiamare una libreria C da 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
non ha funzionato per me. Ho dovuto sostituirlo con un semplice recupero di self.props.width
e self.props.height
. Ho provato per la prima volta a definire la RsvgDimensionData
struttura come descritto sul sito web del cairo, ma senza successo.
Ecco un approccio in cui Inkscape viene chiamato da Python.
Si noti che sopprime alcuni output crufty che Inkscape scrive sulla console (in particolare, stderr e stdout) durante il normale funzionamento senza errori. L'output viene catturato in due variabili stringa out
e 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 '?') )
Ad esempio, durante l'esecuzione di un particolare lavoro sul mio sistema Mac OS, è out
finito per essere:
Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png
(Il file svg di input aveva una dimensione di 339 x 339 pixel.)
In realtà, non volevo dipendere da nient'altro che Python (Cairo, Ink .., ecc.) I miei requisiti dovevano essere il più semplice possibile, al massimo, pip install "savior"
sarebbe bastato un semplice , ecco perché nessuno di quelli sopra lo ha fatto ' è adatto a me.
Ci sono arrivato (andando oltre Stackoverflow nella ricerca). https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Sembra buono, finora. Quindi lo condivido nel caso qualcuno si trovi nella stessa situazione.