如何渲染SVG文件的*部分*内容?

7 投票
5 回答
6983 浏览
提问于 2025-04-15 19:38

我想通过名称来渲染一个svg文件的部分内容,但我实在搞不懂该怎么做(使用python和gtk)。

这是我提到的svg文件:http://david.bellot.free.fr/svg-cards/files/SVG-cards-2.0.1.tar.gz更新:这个文件现在已经不存在了,但你可以在http://svg-cards.sourceforge.net/找到相关信息)

在他的网站上,David说:

你可以通过以下方式绘制一张卡片: 将文件渲染到一个位图上,然后手动裁剪每张卡片,或者通过DOM接口使用卡片的名称。所有卡片都嵌入在一个SVG组中。

我不太明白他所说的DOM接口是什么意思。我做了一些搜索,找到的最接近我想做的结果是:

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"));

不过请注意,这个是针对Qt的,而且甚至不是用python写的。

这是我目前的进展:

#!/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()

当然,这个是没有用的。

我缺少了什么呢?

5 个回答

2

这里稍微挖一下旧的内容,不过ptomato在2010年的回答到2019年在Gtk3中也能用,只需要做一些小改动。下面的代码只会显示3个方块的svg图标。

#!/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()
2

这是我对裁剪空白区域问题的解决方案。这是一个简单的技巧,但效果很好。对于任何想在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", {})
9

用于渲染SVG的GTK库叫做RSVG。它有Python的接口,但这些接口没有文档说明,而且它们没有封装你通常在C语言中使用的rsvg_handle_get_pixbuf_sub()rsvg_handle_render_cairo_sub()这两个函数。根据我的理解,你需要做的就是提取XML节点,就像Adam Crossland建议的那样。要渲染它,你需要做类似下面的事情:

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())

如果你想把它放在gtk.Image中,就这样做;否则就用pixbuf做其他事情。你也可以用handle.render_cairo(cr)将它渲染到Cairo上下文中,其中cr就是你的Cairo上下文。

编辑:

抱歉,刚开始我没有仔细阅读Python接口。_sub()函数是通过id=参数实现的,所以你的程序可以简化成这样:

#!/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()

我测试过这个,确实有效。不过,窗口的大小是整个SVG画布的大小,而被限制在屏幕大小内(这就是我渲染了三颗钻石而不是角落里的黑桃A的原因)。所以你仍然需要找到一种方法来裁剪pixbuf,以便只保留你想要的卡片,但这应该不难。

撰写回答