English 中文(简体)
How do I render *parts* of a svg file?
原标题:

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.

问题回答

Here s my answer to the cropping blank space problem. It s a rough hack but it worked great. This would also serve as a good starting point to get cards for anyone making a card game in python.

import gtk
import rsvg
svg = rsvg.Handle(file="/usr/share/gnome-games-common/cards/gnomangelo_bitmap.svg")
w, h = 202.5, 315
card_names = map(str, range(1,11)) + ["jack", "queen", "king"]
suites = ["club", "diamond", "heart", "spade"]
specials = [{"name":"black_joker","x":0, "y":4}, {"name":"red_joker","x":1, "y":4}, {"name":"back","x":2, "y":4}]
for suite_number, suite in enumerate(suites):
    for card_number, card in enumerate(card_names):
        print "processing", suite, card,  # +card+ _ +suite
        pixbuf = svg.get_pixbuf(id= # +card+ _ +suite)
        pixbuf.subpixbuf(int(w*card_number), int(h*suite_number), int(w), int(h)).save("./"+card+"_"+suite+".png","png", {})
for special in specials:
    print "processing", special["name"]
    pixbuf = svg.get_pixbuf(id= # +special["name"])
    card_number = special["x"]
    suite_number = special["y"]
    pixbuf.subpixbuf(int(w*card_number), int(h*suite_number), int(w), int(h)).save("./"+special["name"]+".png","png", {})

Grave-digging a little bit here, but the answer by ptomato from 2010 also works now in 2019 for Gtk3 with some slight modifications. The below code will render only the 3 of diamonds svg id.

#!/usr/bin/env python

import gi
gi.require_version( Gtk ,  3.0 )
gi.require_version( Rsvg ,  2.0 )

from gi.repository import Gtk, Rsvg

svg = Rsvg.Handle.new_from_file( svg-cards.svg )

pixbuf = svg.get_pixbuf_sub( #3_diamond )

image = Gtk.Image()
image.set_from_pixbuf(pixbuf)
image.show()

window = Gtk.Window()
window.set_title("Foo")
window.connect("destroy", Gtk.main_quit)
window.show()
window.add(image)

Gtk.main()

I believe that what he means by through a DOM interface is that since SVG is XML, you could load the SVG file in minidom, or some other Python XML parser, and extract the XML node with the specific name that you are looking for. That XML node should represent an item that can be rendered.

You can do it by editing the tag. Edit width and height, set the viewBox attribute on the main svg element to the rectangle you want, render, repeat.

See How to show a subsection or "slice" of an SVG graphic? and http://dingoskidneys.com/~dholth/svg/





相关问题
Can Django models use MySQL functions?

Is there a way to force Django models to pass a field to a MySQL function every time the model data is read or loaded? To clarify what I mean in SQL, I want the Django model to produce something like ...

An enterprise scheduler for python (like quartz)

I am looking for an enterprise tasks scheduler for python, like quartz is for Java. Requirements: Persistent: if the process restarts or the machine restarts, then all the jobs must stay there and ...

How to remove unique, then duplicate dictionaries in a list?

Given the following list that contains some duplicate and some unique dictionaries, what is the best method to remove unique dictionaries first, then reduce the duplicate dictionaries to single ...

What is suggested seed value to use with random.seed()?

Simple enough question: I m using python random module to generate random integers. I want to know what is the suggested value to use with the random.seed() function? Currently I am letting this ...

How can I make the PyDev editor selectively ignore errors?

I m using PyDev under Eclipse to write some Jython code. I ve got numerous instances where I need to do something like this: import com.work.project.component.client.Interface.ISubInterface as ...

How do I profile `paster serve` s startup time?

Python s paster serve app.ini is taking longer than I would like to be ready for the first request. I know how to profile requests with middleware, but how do I profile the initialization time? I ...

Pragmatically adding give-aways/freebies to an online store

Our business currently has an online store and recently we ve been offering free specials to our customers. Right now, we simply display the special and give the buyer a notice stating we will add the ...

Converting Dictionary to List? [duplicate]

I m trying to convert a Python dictionary into a Python list, in order to perform some calculations. #My dictionary dict = {} dict[ Capital ]="London" dict[ Food ]="Fish&Chips" dict[ 2012 ]="...

热门标签