#	A quick and simple opengl font library that uses GNU freetype2, written
#	and distributed as part of a tutorial for nehe.gamedev.net.
#	Sven Olsen, 2003
#	Translated to PyOpenGL by Brian Leair, 2004
# 
#



# import freetype
# We are going to use Python Image Library's font handling
# From PIL 1.1.4:
import ImageFont
from OpenGL.GL import *
from OpenGL.GLU import *


# Python 2.2 defines these directly
try:
	True
except NameError:
	True = 1==1
	False = 1==0


def is_font_available (ft, facename):
	""" Returns true if FreeType can find the requested face name 
		Pass the basname of the font e.g. "arial" or "times new roman"
	"""
	if (facename in ft.available_fonts ()):
		return True
	return False


def next_p2 (num):
	""" If num isn't a power of 2, will return the next higher power of two """
	rval = 1
	while (rval<num):
		rval <<= 1
	return rval



def make_dlist (ft, ch, list_base, tex_base_list):
	""" Given an integer char code, build a GL texture into texture_array,
		build a GL display list for display list number display_list_base + ch.
		Populate the glTexture for the integer ch and construct a display
		list that renders the texture for ch.
		Note, that display_list_base and texture_base are supposed
		to be preallocated for 128 consecutive display lists and and 
		array of textures.
	"""

	# //The first thing we do is get FreeType to render our character
	# //into a bitmap.  This actually requires a couple of FreeType commands:
	# //Load the Glyph for our character.
	# //Move the face's glyph into a Glyph object.
	# //Convert the glyph to a bitmap.
	# //This reference will make accessing the bitmap easier
	# - This is the 2 dimensional Numeric array

	# Use our helper function to get the widths of
	# the bitmap data that we will need in order to create
	# our texture.
	glyph = ft.getmask (chr (ch))
	glyph_width, glyph_height = glyph.size 	
	# We are using PIL's wrapping for FreeType. As a result, we don't have 
	# direct access to glyph.advance or other attributes, so we add a 1 pixel pad.
	width = next_p2 (glyph_width + 1)
	height = next_p2 (glyph_height + 1)


	# python GL will accept lists of integers or strings, but not Numeric arrays
	# so, we buildup a string for our glyph's texture from the Numeric bitmap 

	# Here we fill in the data for the expanded bitmap.
	# Notice that we are using two channel bitmap (one for
	# luminocity and one for alpha), but we assign
	# both luminocity and alpha to the value that we
	# find in the FreeType bitmap. 
	# We use the ?: operator so that value which we use
	# will be 0 if we are in the padding zone, and whatever
	# is the the Freetype bitmap otherwise.
	expanded_data = ""
	for j in xrange (height):
		for i in xrange (width):
			if (i >= glyph_width) or (j >= glyph_height):
				value = chr (0)
				expanded_data += value
				expanded_data += value
			else:
				value = chr (glyph.getpixel ((i, j)))
				expanded_data += value
				expanded_data += value

	# -------------- Build the gl texture ------------

	# Now we just setup some texture paramaters.
	ID = glGenTextures (1)
	tex_base_list [ch] = ID
	glBindTexture (GL_TEXTURE_2D, ID)
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

	border = 0
	# Here we actually create the texture itself, notice
	# that we are using GL_LUMINANCE_ALPHA to indicate that
	# we are using 2 channel data.
	glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
		border, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data )

	# With the texture created, we don't need to expanded data anymore
	expanded_data = None



	# --- Build the gl display list that draws the texture for this character ---

	# So now we can create the display list
	glNewList (list_base + ch, GL_COMPILE)

	if (ch == ord (" ")):
		glyph_advance = glyph_width
		glTranslatef(glyph_advance, 0, 0)
		glEndList()
	else:

		glBindTexture (GL_TEXTURE_2D, ID)

		glPushMatrix()

		# // first we need to move over a little so that
		# // the character has the right amount of space
		# // between it and the one before it.
		# glyph_left = glyph.bbox [0]
		# glTranslatef(glyph_left, 0, 0)

		# // Now we move down a little in the case that the
		# // bitmap extends past the bottom of the line 
		# // this is only true for characters like 'g' or 'y'.
		# glyph_descent = glyph.decent
		# glTranslatef(0, glyph_descent, 0)

		# //Now we need to account for the fact that many of
		# //our textures are filled with empty padding space.
		# //We figure what portion of the texture is used by 
		# //the actual character and store that information in 
		# //the x and y variables, then when we draw the
		# //quad, we will only reference the parts of the texture
		# //that we contain the character itself.
		x=float (glyph_width) / float (width)
		y=float (glyph_height) / float (height)

		# //Here we draw the texturemaped quads.
		# //The bitmap that we got from FreeType was not 
		# //oriented quite like we would like it to be,
		# //so we need to link the texture to the quad
		# //so that the result will be properly aligned.		
		if ch == 9:
			# tab emulation
			glPopMatrix()
			glTranslatef(glyph_width * 4, 0, 0)
		else:
			glBegin(GL_QUADS)
			glTexCoord2f(0,0), glVertex2f(0,glyph_height)
			glTexCoord2f(0,y), glVertex2f(0,0)
			glTexCoord2f(x,y), glVertex2f(glyph_width,0)
			glTexCoord2f(x,0), glVertex2f(glyph_width, glyph_height)
			glEnd()
			glPopMatrix()
	
			# Note, PIL's FreeType interface hides the advance from us.
			# Normal PIL clients are rendering an entire string through FreeType, not
			# a single character at a time like we are doing here.
			# Because the advance value is hidden from we will advance
			# the "pen" based upon the rendered glyph's width. This is imperfect.
			# glTranslatef(glyph_width + 0.75, 0, 0)
			glTranslatef(glyph_width, 0, 0)

		# //increment the raster position as if we were a bitmap font.
		# //(only needed if you want to calculate text length)
		# //glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL)

		# //Finnish the display list
		glEndList()

	return

# /// A fairly straight forward function that pushes
# /// a projection matrix that will make object world 
# /// coordinates identical to window coordinates.
def pushScreenCoordinateMatrix():
	glPushAttrib(GL_TRANSFORM_BIT)
	viewport = glGetIntegerv(GL_VIEWPORT)
	glMatrixMode(GL_PROJECTION)
	glPushMatrix()
	glLoadIdentity()
	gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3])
	glPopAttrib()
	return


# Pops the projection matrix without changing the current
# MatrixMode.
def pop_projection_matrix():
	glPushAttrib(GL_TRANSFORM_BIT)
	glMatrixMode(GL_PROJECTION)
	glPopMatrix()
	glPopAttrib()
	return


class font_data:
	def __init__ (self, facename, pixel_height):
		# We haven't yet allocated textures or display lists
		self.m_allocated = False
		self.m_font_height = pixel_height
		self.m_facename = facename

		# Try to obtain the FreeType font
		try:
			ft = ImageFont.truetype (facename, pixel_height)
		except:
			raise ValueError, "Unable to locate true type font '%s'" % (facename)

		# Here we ask opengl to allocate resources for
		# all the textures and displays lists which we
		# are about to create.  
		self.m_list_base = glGenLists (128)

		# Consturct a list of 128 elements. This
		# list will be assigned the texture IDs we create for each glyph
		self.textures = [None] * 128

		# This is where we actually create each of the fonts display lists.
		for i in xrange (128):
			make_dlist (ft, i, self.m_list_base, self.textures);

		self.m_allocated = True


		# //We don't need the face information now that the display
		# //lists have been created, so we free the assosiated resources.
		# Note: Don't need this, as python will decref and dealloc the ft for us.
		ft = None
		return

	def glPrint (self, x, y, lines):
		if not lines:
			return

		# We want a coordinate system where things coresponding to window pixels.
		pushScreenCoordinateMatrix()
	
		# //We make the height about 1.5* that of
		h = float(self.m_font_height)

		glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT)
		glMatrixMode(GL_MODELVIEW)
		glDisable(GL_LIGHTING)
		glEnable(GL_TEXTURE_2D)
		glDisable(GL_DEPTH_TEST)
		glEnable(GL_BLEND)
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

		glListBase(self.m_list_base)
		modelview_matrix = glGetFloatv(GL_MODELVIEW_MATRIX)

		# //This is where the text display actually happens.
		# //For each line of text we reset the modelview matrix
		# //so that the line's text will start in the correct position.
		# //Notice that we need to reset the matrix, rather than just translating
		# //down by h. This is because when each character is
		# //draw it modifies the current matrix so that the next character
		# //will be drawn immediatly after it.  
		i=0
		for line in lines:
			if line:
				glPushMatrix ()
				glLoadIdentity ()
				glTranslatef (x,y-h*i,0);
				glMultMatrixf (modelview_matrix);
	
				self.m_list_base
				for n in line:					
					glCallList(ord(n)+self.m_list_base)
				glPopMatrix()
			i+=1

		glPopAttrib()
		pop_projection_matrix()
		return

	def release (self):
		""" Release the gl resources for this Face.
			(This provides the functionality of KillFont () and font_data::clean ()
		"""
		if (self.m_allocated):
			# Free up the glTextures and the display lists for our face
			glDeleteLists ( self.m_list_base, 128);
			for ID in self.textures:
				glDeleteTextures (ID);
			# Extra defensive. Clients that continue to try and use this object
			# will now trigger exceptions.
			self.list_base = None
			self.m_allocated = False
		return

	def __del__ (self):
		""" Python destructor for when no more refs to this Face object """
		self.release ()
		return


# Unit Test harness if this python module is run directly.
if __name__ == "__main__":
	print "testing availability of freetype font arial\n"
	ft = ImageFont.truetype ("arial.ttf", 15)
	if ft:
		print "Found the TrueType font 'arial.ttf'"
	else:
		print "faild to find the TrueTYpe font 'arial'\n"
