I want to render parts of a svg file by name but for the life of me I cannot figure out how to do so (using python + gtk).
Here's the svg file in question: http://david.bellot.free.fr/svg-cards/files/SVG-cards-2.0.1.tar.gz (Update: this file no longer exists, but you can track it down at http://svg-cards.sourceforge.net/)
On his site, David, says:
You can draw a card either by rendering the file onto a pixmap and clipping each card manually or by using the card's name through a DOM interface. All cards are embedded into a SVG group.
I don't know what he means by a DOM interface. I have done some searching and the best result I found that seems to fit what I want to do is:
QSvgRenderer *renderer = new QSvgRenderer(QLatin1String("SvgCardDeck.svg"));
QGraphicsSvgItem *black = new QGraphicsSvgItem();
QGraphicsSvgItem *red = new QGraphicsSvgItem();
black->setSharedRenderer(renderer);
black->setElementId(QLatin1String("black_joker"));
red->setSharedRenderer(renderer);
red->setElementId(QLatin1String("red_joker"));
Notice however that it is for Qt and is not even written in python.
This is what I have so far:
#!/usr/bin/env python
from __future__ import absolute_import
import cairo
import gtk
import rsvg
from xml import xpath
from xml.dom import minidom
window = gtk.Window()
window.set_title("Foo")
window.set_size_request(256, 256)
window.set_property("resizable", False)
window.set_position(gtk.WIN_POS_CENTER)
window.connect("destroy", gtk.main_quit)
window.show()
document = minidom.parse("cards.svg")
element = xpath.Evaluate("//*[@id='1_club']", document)[0]
xml = element.toxml()
svg = rsvg.Handle()
svg.write(xml)
pixbuf = svg.get_pixbuf()
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()
window.add(image)
gtk.main()
It doesn't work, of course.
What am I missing?
The GTK library for rendering SVG is called RSVG. It has python bindings, but they are undocumented, and they don't wrap the rsvg_handle_get_pixbuf_sub()
and rsvg_handle_render_cairo_sub()
functions which you would normally use for that purpose in C. Here's what you have to do as far as I can tell. You extract the XML node as Adam Crossland suggested. To render it, you have to do something like this:
import gtk
import rsvg
handle = rsvg.Handle()
handle.write(buffer=xml_data)
# xml_data is the XML string for the object you want
image = gtk.Image()
image.set_from_pixbuf(handle.get_pixbuf())
That's if you want it in a gtk.Image
, otherwise do something else with the pixbuf. You can also render it to a Cairo context with handle.render_cairo(cr)
where cr
is your Cairo context.
EDIT:
Sorry, I didn't read the python bindings closely enough at first. The _sub()
functions are implemented using the id=
argument, so your program can boil down to this:
#!/usr/bin/env python
import gtk
import rsvg
window = gtk.Window()
window.set_title("Foo")
window.connect("destroy", gtk.main_quit)
window.show()
svg = rsvg.Handle(file='cards.svg')
pixbuf = svg.get_pixbuf(id='#3_diamond')
image = gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()
window.add(image)
gtk.main()
I tested this and it works. However, the window is the size of the entire SVG canvas, and is clipped to the screen size (which is why I rendered the 3 of diamonds instead of the ace of clubs which is up in the corner.) So you'll still have to find some way to crop the pixbuf around the card that you want, but that shouldn't be too hard.