[cairo-commit] [cairo-www] src/cookbook
Bryce Harrington
bryce at freedesktop.org
Fri Jan 15 13:28:09 PST 2016
src/cookbook/freetypepython.mdwn | 140 +++++++++++++++++++++++++--------------
1 file changed, 92 insertions(+), 48 deletions(-)
New commits:
commit 3a0314a1c426636f2db817f597565c1be0f7e2d8
Author: Lawrence D'Oliveiro <ldo at geek-central.gen.nz>
Date: Wed Dec 23 01:23:19 2015 +0000
fix memory leaks and other cleanup in Python FreeType example
diff --git a/src/cookbook/freetypepython.mdwn b/src/cookbook/freetypepython.mdwn
index a1a48de..5aea77f 100644
--- a/src/cookbook/freetypepython.mdwn
+++ b/src/cookbook/freetypepython.mdwn
@@ -5,85 +5,129 @@ The following snippet uses Python's ctypes module to load a font file using Free
and create a cairo font face from it, using the cairo-ft API that is not part of pycairo yet.
The resulting cairo font face however can be used normally with pycairo.
+ #!/usr/bin/python3
- import ctypes
+ 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 = ctypes.CDLL ("libfreetype.so.6")
- _cairo_so = ctypes.CDLL ("libcairo.so.2")
-
- _cairo_so.cairo_ft_font_face_create_for_ft_face.restype = ctypes.c_void_p
- _cairo_so.cairo_ft_font_face_create_for_ft_face.argtypes = [ ctypes.c_void_p, ctypes.c_int ]
- _cairo_so.cairo_set_font_face.argtypes = [ ctypes.c_void_p, ctypes.c_void_p ]
- _cairo_so.cairo_font_face_status.argtypes = [ ctypes.c_void_p ]
- _cairo_so.cairo_status.argtypes = [ ctypes.c_void_p ]
-
+ _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_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 = ctypes.c_void_p ()
- if FT_Err_Ok != _freetype_so.FT_Init_FreeType (ctypes.byref (_ft_lib)):
- raise "Error initialising FreeType library."
-
- class PycairoContext(ctypes.Structure):
- _fields_ = [("PyObject_HEAD", ctypes.c_byte * object.__basicsize__),
- ("ctx", ctypes.c_void_p),
- ("base", ctypes.c_void_p)]
-
- _surface = cairo.ImageSurface (cairo.FORMAT_A8, 0, 0)
-
+ _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
-
- # create freetype face
- ft_face = ctypes.c_void_p()
- cairo_ctx = cairo.Context (_surface)
- cairo_t = PycairoContext.from_address(id(cairo_ctx)).ctx
-
- if FT_Err_Ok != _freetype_so.FT_New_Face (_ft_lib, filename, faceindex, ctypes.byref(ft_face)):
- raise Exception("Error creating FreeType font face for " + filename)
-
- # create cairo font face for freetype face
- cr_face = _cairo_so.cairo_ft_font_face_create_for_ft_face (ft_face, loadoptions)
- if CAIRO_STATUS_SUCCESS != _cairo_so.cairo_font_face_status (cr_face):
- raise Exception("Error creating cairo font face for " + filename)
-
- _cairo_so.cairo_set_font_face (cairo_t, cr_face)
- if CAIRO_STATUS_SUCCESS != _cairo_so.cairo_status (cairo_t):
- raise Exception("Error creating cairo font face for " + filename)
-
- face = cairo_ctx.get_font_face ()
-
+ #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
if __name__ == '__main__':
-
- face = create_cairo_font_face_for_file ("/usr/share/fonts/dejavu-lgc/DejaVuLGCSerif.ttf", 0)
-
+ face = create_cairo_font_face_for_file("/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf", 0)
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 128, 128)
-
ctx = cairo.Context(surface)
ctx.set_font_face(face)
ctx.set_font_size(30)
ctx.move_to(0, 44)
ctx.show_text("Hello,")
-
ctx.move_to(30, 74)
ctx.show_text("world!")
del ctx
surface.write_to_png("hello.png")
+ #end if
More information about the cairo-commit
mailing list