Re: PATCH cairo-www: fix memory leaks and other cleanup in Python FreeType example
Bryce Harrington <[email protected]>
| Newsgroups | gmane.comp.lib.cairo |
|---|---|
| Message-ID | <[email protected]> |
On Wed, Dec 23, 2015 at 02:24:50PM +1300, Lawrence D'Oliveiro wrote: > >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 Thanks, pushed: To git+ssh://cairographics.org/git/cairo-www 6df850a..3a0314a master -> master > --- > 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 > > -- > cairo mailing list > [email protected] > http://lists.cairographics.org/mailman/listinfo/cairo -- cairo mailing list [email protected] http://lists.cairographics.org/mailman/listinfo/cairo