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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.