[cairo] PATCH cairo-www: fix memory leaks and other cleanup in Python FreeType example

Bryce Harrington bryce at osg.samsung.com
Fri Jan 15 13:28:27 PST 2016


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 <ldo at geek-central.gen.nz>
> 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
> cairo at cairographics.org
> http://lists.cairographics.org/mailman/listinfo/cairo



More information about the cairo mailing list