PATCH cairo-www: fix memory leaks and other cleanup in Python FreeType example
Lawrence D'Oliveiro <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Organization | Geek Central |
| Message-ID | <[email protected]> |
-- cairo mailing list [email protected] http://lists.cairographics.org/mailman/listinfo/cairo
0001-fix-memory-leaks-and-other-cleanup-in-Python-FreeTyp.patch
(text/x-patch, 7.3 KB)
From 13596986985bd6a1fe60e18ed6adb1b41d36257c Mon Sep 17 00:00:00 2001 From: Lawrence D'Oliveiro <[email protected]> Date: Wed, 23 Dec 2015 01:23:19 +0000 Subject: [PATCH] fix memory leaks and other cleanup in Python FreeType example --- src/cookbook/freetypepython.mdwn | 140 +++++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 48 deletions(-) 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 -- 2.6.0