【问题标题】:Error with Python ctypes and librsvgPython ctypes 和 librsvg 出错
【发布时间】:2011-09-02 19:41:09
【问题描述】:

我正在尝试用 Python 的 ctypes 包装 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)

你知道这有什么问题吗?

【问题讨论】:

  • 作为 OSX 的直接解决方案,在 32 位下运行代码应该可以“解决”问题。 arch -i386 python x.py

标签: python ctypes cairo pycairo librsvg


【解决方案1】:

问题是没有定义返回类型的规范;在这种情况下,仅在结果上使用c_void_p 不足以解决问题。你需要放

librsvg.rsvg_handle_new_from_file.restype = c_void_p

在适当的地方。然后它(也)可以在 OSX 中以 32 位或 64 位运行。

但我发现,在从文件创建句柄时,增加基本包装以处理可能出现的错误更有帮助。以下是执行此操作的基本包装器。它还以几乎相同的方式复制标准 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://stackoverflow.com/a/14928770/1832154

【讨论】:

  • 我会在听说有人成功使用此方法后立即投票,否则我无法验证这一点,不是 OSX 用户
  • @JamesHurford 这有点矛盾,因为我自己用过。所以你已经“听到”有人说它成功了。
  • 是的,你可能是对的。当我阅读答案时,我的时间很短,所以我很抱歉。给我点个赞吧。很高兴听到有人像我之前所说的那样解决了这个问题
  • 我试图在 OS X 上运行一个依赖 pyrsvg 的脚本 (github.com/skagedal/svgclip),首先我尝试安装 gnome-python-desktop 或类似的东西明白了,但我很快就发现这是一件非常困难的事情。使用您的代码要简单得多。非常感谢。
  • 我刚刚在 OS X 10.11 上使用 Python 3.5 测试了这段代码,它运行良好。问题确实是在rsvg_handle_new_from_filersvg_handle_render_cairo 函数上都缺少正确的类型声明。谢谢@mmgp。为我省去了很多麻烦。
【解决方案2】:

librsvg.rsvg_handle_render_cairo 需要指针,而是获取整数。不确定这里的整个故事,但这个修改至少没有段错误。

试试这个

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

请注意,我将两个参数包装在 c_void_p 中,以使它们成为 void * 指针。不理想,但似乎有效。

【讨论】:

  • 它非常适合我。它只是一个段错误消息,还是您实际上将任何有用的信息打印到控制台?我怀疑你使用的 librsvg C 库版本有问题。
  • 我正在使用 librsvg-2.so.2.34.0 和 libgobject-2.0.so.0.2800.8
  • 我还注意到您似乎使用的是 Apple Mac,我正在运行 Gentoo linux amd64。这可能会为正在发生的事情提供线索?即它可能是特定于 Mac 的问题。
  • 您可以尝试更新到 librsvg-2.34.0 或更高版本,看看是否可行。如果较新的版本不适合您,请提交特定于 Mac 的错误并尝试较早的版本。如果这些都不起作用,那么您可以尝试从开发人员 librsvg 使用的存储库中获取最新版本。
  • @ahojnnes 这不是错误,包装器只是粗心完成。需要指定restype,仅在结果上使用c_void_p 是不够的。请参阅另一个答案(如果您碰巧在某个时候重新访问 SO)。
猜你喜欢
  • 1970-01-01
  • 2011-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-29
  • 2021-08-26
  • 1970-01-01
  • 2022-11-20
相关资源
最近更新 更多