Cairo missing attribute FONT_SLANT_NORMAL

I’m using Cairo in a GTK application. It works fine drawing boxes etc. but when I try to “draw” as below I get AttributeError: 'gi.repository.cairo' object has no attribute 'FONT_SLANT_NORMAL'

This is my code

import gi

gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')

from gi.repository import Adw, Gtk, cairo

class CairoTutorial():

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def text(self, area, cr, width, height):
        cr.set_source_rgb(0.0, 0.0, 0.0)
        cr.select_font_face("Georgia",
                cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
        cr.set_font_size(12)
        x_bearing, y_bearing, width, height = cr.text_extents("a")[:4]
        cr.move_to(5 - width / 2 - x_bearing, 5 - height / 2 - y_bearing)
        cr.show_text("a")

Is there something wrong with my imports?

Cairo cannot be used through the introspection API, because Cairo is not a GObject-based library, and does not have proper introspection.

You want to use the pycairo bindings.

Importing pycairo is compatible with GTK used through pygobject.

Hi Emmanuele,

not sure what you mean with “proper”, but cairo does have introspection
support:

https://gitlab.gnome.org/GNOME/gobject-introspection/-/blob/main/gir/cairo-1.0.gir.in

In that specific case, the font slant is inside the FontSlant
enumeration (i.e. with Lua bindings I can access that value with
cairo.FontSlant.NORMAL), so maybe it is just a syntax issue.

Ciao.

It really does not. :wink:

The cairo-1.0.gir file is only there to hold the GType for signals and properties; it does not cover the entirety of the enumerations, and it does not cover the entirety of the API. This is because GTK depends on some of the Cairo types, and thus we need a basic Cairo introspection file in order to generate GTK’s own introspection data.

Cairo simply cannot be used through introspection, because Cairo does not use GObject and the GType type system do describe the inheritance of its types. For instance, you cannot use the cairo_surface_t API with a cairo_image_surface_t via introspection.

Sorry but I need to clarify that. In another project I even proposed to
adopt the cairo approach [1], i.e. leverage gobject-introspection on a
library not based on GObject, and I need to know if there is a big
blocker I do not see right now.

I’m aware cairo is not GObject based and, AFAIK, this is the reason
cairo-gobject [2] has been written. If any enumeration is missing, this
is a bug.

I’m also aware cairo needs special care on the bindings side to recreate
the missing hierarchy (see e.g. the LGI overrides for cairo [3]) and
AFAIK this is the biggest issue. But, if implemented, I find cairo
perfectly usable with bindings. In my main public project [4] I use it
quite extensively directly in Lua on an OpenResty webserver.

[1] Implement Library in C++ (additionally) (#3) · Issues · EtherLab / EtherCAT · GitLab
[2] cairo-gobject\util - cairo - cairo's central repository (mirrored from https://gitlab.freedesktop.org/cairo/cairo)
[3] lgi/cairo.lua at master · lgi-devs/lgi · GitHub
[4] https://adg.entidi.com/

After a second read, that statement sounds quite misleading: it is true that the ADG library uses cairo extensively, but much more on the C side. In the bindings side it is not used extensively: mainly matrix manipulation and enumeration types.

The main blocker is that you cannot handle type hierarchies in gobject-introspection unless you use instance types, like GObject; at most you can use GTypeInstance, but you should really only do that as a last resort.

What cairo-gobject does is exposing its own types as boxed types, not to have bindings or introspection data, but because GTK uses Cairo types in signals and properties, which require registration into the GObject type system, and the obvious way to do that is to use GBoxedType. Boxed types, though, cannot have derived types. For instance: a cairo_image_surface_t is, according to Cairo, a derived type of cairo_surface_t—i.e. you can call any function that operates on a cairo_surface_t* with a pointer to a cairo_image_surface_t; but according to GObject (and GObject-introspection), a cairo_image_surface_t and a cairo_surface_t are completely unrelated types, and they cannot be used interchangeably.

The whole point of cairo-1.0.gir is to merely provide enough types for generating the introspection data for GTK (and, of course, any other introspection data that depends on Cairo types); it is not meant to be used as the way to use the Cairo API. Any attempt at doing so is automatically and irreparably broken.

If you want to use the Cairo API you will need ad hoc native bindings.

Now, that does not mean you cannot use GObject-introspection with libraries that only expose boxed types and enumeration types; for instance, you can use introspection with Graphene. The main difference between Graphene and Cairo is that the former has a completely flat type hierarchy: every single type is independent from the others. This allows mapping plain C types to GBoxedType, and thus to provide an API that can be introspected.

1 Like

In that article the link to the library points to a book on amazon: is this a typo or a joke I do not grasp?

That is inline with how I think this stuff works. Thank you for the answer and the links!

It’s a joke: the book opens with a similar sentence. :smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.