Corner mitring python script for FontLab

I created a mitred corner algorithm for my build scripts to subtly mitre any extra sharp corners that are created when I remove overlaps. For more information on why it's a good idea to not flatten your glyphs until right before you export, see this post on using build scripts to author the final font files.

Click "Read More" for the script

"""Mitre Glyph: 

mitreSize : Length of the segment created by the mitre. The default is 4.
maxAngle :  Maximum angle in radians at which nodes will be mitred. The default is .9 (about 50 degrees).
            Works for both inside and outside angles

"""

import math
	
def getContourRange(nid,g):
	cID = g.FindContour(nid)
	cStart = g.GetContourBegin(cID)
	cEnd = cStart + g.GetContourLength(cID) - 1
	return cStart,cEnd

def getNextNode(nid,g):
	cStart,cEnd = getContourRange(nid,g)
	if nid == cEnd:
		return g[cStart]
	else:
		return g[nid + 1]
	
def getPrevNode(nid,g):
	cStart,cEnd = getContourRange(nid,g)
	if nid == cStart:
		return g[cEnd]
	else:
		return g[nid - 1]

def normalizeVector(p):
	m = getMagnitude(p);
	if m != 0:
		return p*(1/m)
	else:
		return Point(0,0)

def getMagnitude(p):
	return math.sqrt(p.x*p.x + p.y*p.y)
	
def getDistance(v1,v2):
	return getMagnitude(Point(v1.x - v2.x, v1.y - v2.y))

def getAngle(v1,v2):
	angle = math.atan2(v1.y,v1.x) - math.atan2(v2.y,v2.x)
	return (angle + (2*math.pi)) % (2*math.pi)
	
def getNodeVectors(g,nid):
	n = g[nid]
	p = Point(n.x, n.y)
	nn = getNextNode(nid, g)
	pn = getPrevNode(nid, g)
	
	nVect = Point(-p.x + nn.x, -p.y + nn.y)
	pVect = Point(-p.x + pn.x, -p.y + pn.y)
		
	if nn.type == nCURVE:
		nn = Point(nn[1].x,nn[1].y)
	else:
		nn = Point(nn.x,nn.y)
		
	if n.type == nCURVE:
		pn = Point(n[2].x,n[2].y)
	else:
		pn = Point(pn.x,pn.y)
	
	nVect = Point(-p.x + nn.x, -p.y + nn.y)
	pVect = Point(-p.x + pn.x, -p.y + pn.y)
	return pVect,nVect
	

def mitreCorner(g, nid, mitreSize = 5., maxAngle = .9):
	
	pVect,nVect = getNodeVectors(g,nid)
	
	# dont mitre if segment is too short
	if abs(getMagnitude(pVect)) < mitreSize * 2 or abs(getMagnitude(nVect)) < mitreSize * 2:
		return
	
	angle = getAngle(nVect,pVect)
	nVect = normalizeVector(nVect)
	pVect = normalizeVector(pVect)
	
	# only mitre corners sharper than maxAngle
	if angle > maxAngle and angle < math.pi * 2 - maxAngle:
		return
	
	radius = mitreSize / abs(getDistance(nVect,pVect))
	
	offset1 = Point(round(pVect.x * radius), round(pVect.y * radius))
	offset2 = Point(round(nVect.x * radius), round(nVect.y * radius))
	
	g.Insert(Node(nLINE, Point(g.nodes[nid].x, g.nodes[nid].y)), nid+1)
	
	n1 = g.nodes[nid]
	n2 = g.nodes[nid + 1]
	
	n1[0].x += offset1.x
	n1[0].y += offset1.y
	n2[0].x += offset2.x
	n2[0].y += offset2.y
	
	return True
	
def mitreGlyph(g,mitreSize,maxAngle):
	fl.SetUndo()
	i = 0
	while i < len(g.nodes):
		n = g[i]
		if mitreCorner(g,i,mitreSize,maxAngle):
			i+=1
		i += 1
	fl.UpdateGlyph()
	
mitreGlyph(fl.glyph,4.,.9)

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.

More information about formatting options

Image CAPTCHA
Copy the characters (respecting upper/lower case) from the image.