summaryrefslogtreecommitdiff
path: root/tools/about/ttf.py
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2025-06-01 13:27:50 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2025-06-01 13:27:50 (GMT)
commit07333983ff99b22994a79c8a7cbf4fad0b80158f (patch)
treeea38a27b114212b662f31feadfe2db1347a53148 /tools/about/ttf.py
parentbde358e3a0b8333fd796c89a3be998d8ad5b1826 (diff)
Build a static image for the About dialog box background.gtk4
Diffstat (limited to 'tools/about/ttf.py')
-rw-r--r--tools/about/ttf.py111
1 files changed, 111 insertions, 0 deletions
diff --git a/tools/about/ttf.py b/tools/about/ttf.py
new file mode 100644
index 0000000..eb6c027
--- /dev/null
+++ b/tools/about/ttf.py
@@ -0,0 +1,111 @@
+
+# Source: https://www.cairographics.org/cookbook/freetypepython/
+
+import ctypes as ct
+import cairo
+
+_initialized = False
+def create_cairo_font_face_for_file (filename, faceindex=0, loadoptions=0):
+ "given the name of a font file, and optional faceindex to pass to FT_New_Face" \
+ " and loadoptions to pass to cairo_ft_font_face_create_for_ft_face, creates" \
+ " a cairo.FontFace object that may be used to render text with that font."
+ global _initialized
+ global _freetype_so
+ global _cairo_so
+ global _ft_lib
+ global _ft_destroy_key
+ global _surface
+
+ CAIRO_STATUS_SUCCESS = 0
+ FT_Err_Ok = 0
+
+ if not _initialized:
+ # find shared objects
+ _freetype_so = ct.CDLL("libfreetype.so.6")
+ _cairo_so = ct.CDLL("libcairo.so.2")
+ _cairo_so.cairo_ft_font_face_create_for_ft_face.restype = ct.c_void_p
+ _cairo_so.cairo_ft_font_face_create_for_ft_face.argtypes = [ ct.c_void_p, ct.c_int ]
+ _cairo_so.cairo_font_face_get_user_data.restype = ct.c_void_p
+ _cairo_so.cairo_font_face_get_user_data.argtypes = (ct.c_void_p, ct.c_void_p)
+ _cairo_so.cairo_font_face_set_user_data.argtypes = (ct.c_void_p, ct.c_void_p, ct.c_void_p, ct.c_void_p)
+ _cairo_so.cairo_set_font_face.argtypes = [ ct.c_void_p, ct.c_void_p ]
+ _cairo_so.cairo_font_face_status.argtypes = [ ct.c_void_p ]
+ _cairo_so.cairo_font_face_destroy.argtypes = (ct.c_void_p,)
+ _cairo_so.cairo_status.argtypes = [ ct.c_void_p ]
+ # initialize freetype
+ _ft_lib = ct.c_void_p()
+ status = _freetype_so.FT_Init_FreeType(ct.byref(_ft_lib))
+ if status != FT_Err_Ok :
+ raise RuntimeError("Error %d initializing FreeType library." % status)
+ #end if
+
+ class PycairoContext(ct.Structure):
+ _fields_ = \
+ [
+ ("PyObject_HEAD", ct.c_byte * object.__basicsize__),
+ ("ctx", ct.c_void_p),
+ ("base", ct.c_void_p),
+ ]
+ #end PycairoContext
+
+ _surface = cairo.ImageSurface(cairo.FORMAT_A8, 0, 0)
+ _ft_destroy_key = ct.c_int() # dummy address
+ _initialized = True
+ #end if
+
+ ft_face = ct.c_void_p()
+ cr_face = None
+ try :
+ # load FreeType face
+ status = _freetype_so.FT_New_Face(_ft_lib, filename.encode("utf-8"), faceindex, ct.byref(ft_face))
+ if status != FT_Err_Ok :
+ raise RuntimeError("Error %d creating FreeType font face for %s" % (status, filename))
+ #end if
+
+ # create Cairo font face for freetype face
+ cr_face = _cairo_so.cairo_ft_font_face_create_for_ft_face(ft_face, loadoptions)
+ status = _cairo_so.cairo_font_face_status(cr_face)
+ if status != CAIRO_STATUS_SUCCESS :
+ raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename))
+ #end if
+ # Problem: Cairo doesn't know to call FT_Done_Face when its font_face object is
+ # destroyed, so we have to do that for it, by attaching a cleanup callback to
+ # the font_face. This only needs to be done once for each font face, while
+ # cairo_ft_font_face_create_for_ft_face will return the same font_face if called
+ # twice with the same FT Face.
+ # The following check for whether the cleanup has been attached or not is
+ # actually unnecessary in our situation, because each call to FT_New_Face
+ # will return a new FT Face, but we include it here to show how to handle the
+ # general case.
+ if _cairo_so.cairo_font_face_get_user_data(cr_face, ct.byref(_ft_destroy_key)) == None :
+ status = _cairo_so.cairo_font_face_set_user_data \
+ (
+ cr_face,
+ ct.byref(_ft_destroy_key),
+ ft_face,
+ _freetype_so.FT_Done_Face
+ )
+ if status != CAIRO_STATUS_SUCCESS :
+ raise RuntimeError("Error %d doing user_data dance for %s" % (status, filename))
+ #end if
+ ft_face = None # Cairo has stolen my reference
+ #end if
+
+ # set Cairo font face into Cairo context
+ cairo_ctx = cairo.Context(_surface)
+ cairo_t = PycairoContext.from_address(id(cairo_ctx)).ctx
+ _cairo_so.cairo_set_font_face(cairo_t, cr_face)
+ status = _cairo_so.cairo_font_face_status(cairo_t)
+ if status != CAIRO_STATUS_SUCCESS :
+ raise RuntimeError("Error %d creating cairo font face for %s" % (status, filename))
+ #end if
+
+ finally :
+ _cairo_so.cairo_font_face_destroy(cr_face)
+ _freetype_so.FT_Done_Face(ft_face)
+ #end try
+
+ # get back Cairo font face as a Python object
+ face = cairo_ctx.get_font_face()
+ return face
+#end create_cairo_font_face_for_file