Python ctypes和librsvg出现错误

4
我正在尝试使用ctypes为Python包装librsvg的基本功能,但是我遇到了段错误。
C:
// pycairo excerpt
typedef struct {
  PyObject_HEAD
  cairo_t *ctx;
  PyObject *base; /* base object used to create context, or NULL */
} PycairoContext;

// librsvg excerpt
RsvgHandle * rsvg_handle_new_from_file (const gchar * file_name, GError ** error);
// ...
gboolean rsvg_handle_render_cairo (RsvgHandle * handle, cairo_t * cr);

Python ctypes:
from ctypes import *
from ctypes import util


librsvg = cdll.LoadLibrary('/brew/lib/librsvg-2.2.dylib')
libgobject = cdll.LoadLibrary('/brew/lib/libgobject-2.0.dylib')

libgobject.g_type_init()


class RSVGDimensionData(Structure):

    _fields_ = (
        ('width', c_int),
        ('height', c_int),
        ('em', c_double),
        ('ex', c_double)
    )

class PycairoContext(Structure):

    _fields_ = (
        ('PyObject_HEAD', c_byte * object.__basicsize__),
        ('ctx', c_void_p),
        ('base', c_void_p)
    )


class RSVGHandle(object):

    def __init__(self, path):
        self.path = path
        self.error = ''
        self.handle = librsvg.rsvg_handle_new_from_file(self.path, self.error)

    def render_cairo(self, context):
        context.save()
        z = PycairoContext.from_address(id(context))
        librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
        context.restore()


import cairo

h = RSVGHandle('bank.svg')
s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 100, 100)
ctx = cairo.Context(s)


# segmentation fault....
h.render_cairo(ctx)

错误发生在这一行:librsvg.rsvg_handle_render_cairo(self.handle, z.ctx) 有任何关于这个错误的想法吗?

1
作为OSX的即时解决方案,以32位运行您的代码应该可以“解决”问题。arch -i386 python x.py - mmgp
2个回答

6
问题在于返回类型的规范未定义;仅使用c_void_p作为结果是无法解决这种情况的问题。你需要添加……
librsvg.rsvg_handle_new_from_file.restype = c_void_p

将其放置在合适的位置。然后它也可以在32位或64位的OSX上运行。

但我发现增加基本包装以处理从文件创建句柄时可能出现的错误更有帮助。以下是一个基本包装器,可以做到这一点。它还以基本相同的方式复制了标准rsvg绑定的基本用法。

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

示例用法可在https://dev59.com/R27Xa4cB1Zd3GeqPrZWs#14928770中查看。


只要我听到有人成功使用这种方法,我就会点赞,否则我无法验证,因为我不是OSX用户。 - James Hurford
@JamesHurford,这有点矛盾,因为我自己已经使用过它。所以你已经“听到”有人说它成功了。 - mmgp
是的,你可能是对的。当我阅读答案时我时间很紧,所以请原谅我。这是我的点赞。很高兴听到有人解决了这个问题,就像我之前说的一样。 - James Hurford
1
我试图在OS X上运行一个脚本(https://github.com/skagedal/svgclip),它依赖于pyrsvg,一开始我尝试安装gnome-python-desktop或类似的东西来获取它,但很快发现这是非常困难的事情。使用你的代码要简单得多。非常感谢。 - Jorge Vargas
1
我刚在OS X 10.11上使用Python 3.5测试了这段代码,它完美地运行了。问题确实是rsvg_handle_new_from_filersvg_handle_render_cairo函数缺乏正确的类型声明。谢谢@mmgp,帮我省去了很多麻烦。 - Rafa Viotti

3

librsvg.rsvg_handle_render_cairo函数需要指针作为参数,但实际传入的是整数。具体情况不确定,但至少这种修改可以避免段错误。

请尝试以下代码:

 librsvg.rsvg_handle_render_cairo(c_void_p(self.handle), c_void_p(z.ctx))

注意,我将这两个参数用c_void_p封装成void *指针。虽然不是很理想,但它似乎起作用了。


1
我正在使用 librsvg-2.so.2.34.0 和 libgobject-2.0.so.0.2800.8。 - James Hurford
我还注意到你似乎在使用苹果Mac电脑,而我正在运行Gentoo Linux amd64。这可能会给我们提供一些线索,即问题可能是与Mac相关的。 - James Hurford
1
你可以尝试更新到librsvg-2.34.0或更高版本,看看是否可行。如果较新版本无法使用,请针对Mac文件一个特定的错误,并尝试更早版本。如果所有这些都无效,则可以尝试从开发人员librsvg使用的存储库获取最新版本。 - James Hurford
1
@ahojnnes 这不是一个 bug,只是封装处理得有些粗心。需要指定 restype,仅在结果上使用 c_void_p 是不够的。如果你回到 SO,请查看其他答案。 - mmgp
我很高兴有人使用OSX能够解决这个问题,虽然我不能验证,因为我不使用OSX。 - James Hurford
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接