如何使用pycairo创建字母间距属性?

41
我正在使用Python3.7通过GObject使用Pango + Cairo渲染文本,并希望创建一个属性并将其附加到我的Pango布局以设置字母间距。在Pango的gnome文档中,我可以看到应该有一个名为pango_attr_letter_spacing_new的函数(自v1.6起)。但是,如果我运行Pango.attr_letter_spacing_new,我会收到错误:
AttributeError: 'gi.repository.Pango' object has no attribute 'attr_letter_spacing_new'

感觉有点奇怪,因为我可以使用 pango_attr_type_get_name,而这个函数应该只能在 v1.22 及以上的版本中可用。

我找到了一种解决方法,可以使用 markup 以及 <span letter_spacing="1234">,但我更想避免这种方法。

最小的“工作”示例

# pip install pycairo==1.18.0 pygobject==3.32.0

import cairo
import gi
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Pango, PangoCairo

width, height = 328, 48

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
context = cairo.Context(surface)
layout = PangoCairo.create_layout(context)

font_desc = Pango.font_description_from_string('Sans, 40px')
layout.set_font_description(font_desc)

# What I can do
layout.set_markup(f'<span letter_spacing="{1024 * 10}">Hello World</span>')

# What I would like to do
if False:
    letter_spacing_attr = Pango.attr_letter_spacing_new(1024 * 10)

    attr_list = Pango.AttrList()
    attr_list.insert(letter_spacing_attr)
    layout.set_attributes(attr_list)

    layout.set_text('Hello World')

PangoCairo.show_layout(context, layout)

with open('help-me.png', 'wb') as image_file:
    surface.write_to_png(image_file)

手动创建LetterSpacing属性

我已经找到了枚举值Pango.AttrType.LETTER_SPACING,这使我能够像这样做:

c = Pango.AttrClass()
c.type = Pango.AttrType.LETTER_SPACING
a = Pango.Attribute()
a.init(c)

然而,我没有找到设置它值的方法,这让我觉得这种方法是错误的:|
将其插入到Pango.AttrList中会导致错误(并不奇怪),并且在下次执行Pango操作时使python进程崩溃。
** (process:17183): WARNING **: 12:00:56.985: (gi/pygi-struct-marshal.c:287):pygi_arg_struct_from_py_marshal: runtime check failed: (g_type_is_a (g_type, G_TYPE_VARIANT) || !is_pointer || transfer == GI_TRANSFER_NOTHING)

其他线索

...很遗憾,这些线索都没有带来任何结果 :(

  • pygtk列出一个名为pango.AttrLetterSpacing的函数
    • Pango.AttrLetterSpacing => 'gi.repository.Pango'对象没有属性'AttrLetterSpacing'
    • Pango.Attrbute.LetterSpacing => 'Attribute'类型对象没有属性'LetterSpacing'
  • Vala的pango包文档(似乎也使用GObject)还显示了attr_letter_spacing_new函数--这并没有太大帮助,但表明该函数应该可以通过GObject获得,尽管我还没有尝试。

2
这个页面查看pygobject文档,似乎attr_letter_spacing_new函数没有Python等效版本。这与这个问题中所说的在pygtk -> pygobject转换中丢失的内容相符。 - CodeSurgeon
1
可能会导致解决方案的一些进一步研究。Pitivi视频编辑器项目使用PyGObject。他们显然通过这里展示的方式绕过了有缺陷的Pango.AttrList类。 - CodeSurgeon
1
@CodeSurgeon 这很有趣,尽管他们基本上完全重新实现了它(“FIXME Fix Pango so we do NOT need that dirty reimplementation Pango.AttrList”)。原始问题中建议的<span>解决方法看起来更加清晰(特别是当您只需要此属性而不是所有属性时)。 - Michal Čihař
使用标记的一个显著问题是需要确保输入已转义。在我们的情况下,输入直接来自用户,所以我想要另一种方法 :) - RasmusWL
@RasmusWL,你能告诉我你安装了哪个版本的Pango吗?我刚刚尝试了你的示例(将标有What I would like to doif设置为True),它运行得很好。我正在使用1.44.5版本。我一直在阅读关于GObject Introspection如何工作的内容,对你所面临的问题非常感兴趣。 - caxcaxcoatl
显示剩余3条评论
1个回答

2

看起来这个问题已经被解决了,使用pycairo 1.20.0和PyGObject 3.40.1:

import cairo
import gi
gi.require_version('Pango', '1.0')
gi.require_version('PangoCairo', '1.0')
from gi.repository import Pango, PangoCairo

width, height = 328, 48

surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
context = cairo.Context(surface)
layout = PangoCairo.create_layout(context)

font_desc = Pango.font_description_from_string('Sans, 40px')
layout.set_font_description(font_desc)

letter_spacing_attr = Pango.attr_letter_spacing_new(1024 * 10)

attr_list = Pango.AttrList()
attr_list.insert(letter_spacing_attr)
layout.set_attributes(attr_list)

layout.set_text('Hello World')

PangoCairo.show_layout(context, layout)

with open('help-me.png', 'wb') as image_file:
    surface.write_to_png(image_file)

已确认可正常工作(pycairo 1.20.1,PyGObject 3.42.0) - RasmusWL

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