# Source code for fontTools.misc.symfont

    from fontTools.pens.basePen import BasePen
    from functools import partial
    from itertools import count
    import sympy as sp
    import sys

    n = 3 # Max Bezier degree; 3 for cubic, 2 for quadratic

    t, x, y = sp.symbols('t x y', real=True)
    c = sp.symbols('c', real=False) # Complex representation instead of x/y

    X = tuple(sp.symbols('x:%d'%(n+1), real=True))
    Y = tuple(sp.symbols('y:%d'%(n+1), real=True))
    P = tuple(zip(*(sp.symbols('p:%d[%s]'%(n+1,w), real=True) for w in '01')))
    C = tuple(sp.symbols('c:%d'%(n+1), real=False))

    # Cubic Bernstein basis functions
    BinomialCoefficient = [(1, 0)]
    for i in range(1, n+1):
    	last = BinomialCoefficient[-1]
    	this = tuple(last[j-1]+last[j] for j in range(len(last)))+(0,)
    	BinomialCoefficient.append(this)
    BinomialCoefficient = tuple(tuple(item[:-1]) for item in BinomialCoefficient)
    del last, this

    BernsteinPolynomial = tuple(
    	tuple(c * t**i * (1-t)**(n-i) for i,c in enumerate(coeffs))
    	for n,coeffs in enumerate(BinomialCoefficient))

    BezierCurve = tuple(
    	tuple(sum(P[i][j]*bernstein for i,bernstein in enumerate(bernsteins))
    		for j in range(2))
    	for n,bernsteins in enumerate(BernsteinPolynomial))
    BezierCurveC = tuple(
    	sum(C[i]*bernstein for i,bernstein in enumerate(bernsteins))
    	for n,bernsteins in enumerate(BernsteinPolynomial))

    def green(f, curveXY):
    	f = -sp.integrate(sp.sympify(f), y)
    	f = f.subs({x:curveXY[0], y:curveXY[1]})
    	f = sp.integrate(f * sp.diff(curveXY[0], t), (t, 0, 1))
    	return f

    class _BezierFuncsLazy(dict):

    	def __init__(self, symfunc):
    		self._symfunc = symfunc
    		self._bezfuncs = {}

    	def __missing__(self, i):
    		args = ['p%d'%d for d in range(i+1)]
    		f = green(self._symfunc, BezierCurve[i])
    		f = sp.gcd_terms(f.collect(sum(P,()))) # Optimize
    		return sp.lambdify(args, f)

    class GreenPen(BasePen):

    	_BezierFuncs = {}

    	@classmethod
    	def _getGreenBezierFuncs(celf, func):
    		funcstr = str(func)
    		if not funcstr in celf._BezierFuncs:
    			celf._BezierFuncs[funcstr] = _BezierFuncsLazy(func)
    		return celf._BezierFuncs[funcstr]

    	def __init__(self, func, glyphset=None):
    		BasePen.__init__(self, glyphset)
    		self._funcs = self._getGreenBezierFuncs(func)
    		self.value = 0

    	def _moveTo(self, p0):
    		self.__startPoint = p0

    	def _closePath(self):
    		p0 = self._getCurrentPoint()
    		if p0 != self.__startPoint:
    			self._lineTo(self.__startPoint)

    	def _endPath(self):
    		p0 = self._getCurrentPoint()
    		if p0 != self.__startPoint:
    			# Green theorem is not defined on open contours.
    			raise NotImplementedError

    	def _lineTo(self, p1):
    		p0 = self._getCurrentPoint()
    		self.value += self._funcs[1](p0, p1)

    	def _qCurveToOne(self, p1, p2):
    		p0 = self._getCurrentPoint()
    		self.value += self._funcs[2](p0, p1, p2)

    	def _curveToOne(self, p1, p2, p3):
    		p0 = self._getCurrentPoint()
    		self.value += self._funcs[3](p0, p1, p2, p3)

    # Sample pens.
    # Do not use this in real code.
    # Use fontTools.pens.momentsPen.MomentsPen instead.
    AreaPen = partial(GreenPen, func=1)
    MomentXPen = partial(GreenPen, func=x)
    MomentYPen = partial(GreenPen, func=y)
    MomentXXPen = partial(GreenPen, func=x*x)
    MomentYYPen = partial(GreenPen, func=y*y)
    MomentXYPen = partial(GreenPen, func=x*y)

    def printGreenPen(penName, funcs, file=sys.stdout, docstring=None):

    	if docstring is not None:
    		print('"""%s"""' % docstring)

    	print(
    '''from fontTools.pens.basePen import BasePen, OpenContourError
    try:
    	import cython
    except ImportError:
    	# if cython not installed, use mock module with no-op decorators and types
    	from fontTools.misc import cython

    if cython.compiled:
    	# Yep, I'm compiled.
    	COMPILED = True
    else:
    	# Just a lowly interpreted script.
    	COMPILED = False

    __all__ = ["%s"]

    class %s(BasePen):

    	def __init__(self, glyphset=None):
    		BasePen.__init__(self, glyphset)
    '''% (penName, penName), file=file)
    	for name,f in funcs:
    		print('		self.%s = 0' % name, file=file)
    	print('''
    	def _moveTo(self, p0):
    		self.__startPoint = p0

    	def _closePath(self):
    		p0 = self._getCurrentPoint()
    		if p0 != self.__startPoint:
    			self._lineTo(self.__startPoint)

    	def _endPath(self):
    		p0 = self._getCurrentPoint()
    		if p0 != self.__startPoint:
    			# Green theorem is not defined on open contours.
    			raise OpenContourError(
    							"Green theorem is not defined on open contours."
    			)
    ''', end='', file=file)

    	for n in (1, 2, 3):

    		subs = {P[i][j]: [X, Y][j][i] for i in range(n+1) for j in range(2)}
    		greens = [green(f, BezierCurve[n]) for name,f in funcs]
    		greens = [sp.gcd_terms(f.collect(sum(P,()))) for f in greens] # Optimize
    		greens = [f.subs(subs) for f in greens] # Convert to p to x/y
    		defs, exprs = sp.cse(greens,
    				     optimizations='basic',
    				     symbols=(sp.Symbol('r%d'%i) for i in count()))

    		print()
    		for name,value in defs:
    			print('	@cython.locals(%s=cython.double)' % name, file=file)
    		if n == 1:
    			print('''\
    	@cython.locals(x0=cython.double, y0=cython.double)
    	@cython.locals(x1=cython.double, y1=cython.double)
    	def _lineTo(self, p1):
    		x0,y0 = self._getCurrentPoint()
    		x1,y1 = p1
    ''', file=file)
    		elif n == 2:
    			print('''\
    	@cython.locals(x0=cython.double, y0=cython.double)
    	@cython.locals(x1=cython.double, y1=cython.double)
    	@cython.locals(x2=cython.double, y2=cython.double)
    	def _qCurveToOne(self, p1, p2):
    		x0,y0 = self._getCurrentPoint()
    		x1,y1 = p1
    		x2,y2 = p2
    ''', file=file)
    		elif n == 3:
    			print('''\
    	@cython.locals(x0=cython.double, y0=cython.double)
    	@cython.locals(x1=cython.double, y1=cython.double)
    	@cython.locals(x2=cython.double, y2=cython.double)
    	@cython.locals(x3=cython.double, y3=cython.double)
    	def _curveToOne(self, p1, p2, p3):
    		x0,y0 = self._getCurrentPoint()
    		x1,y1 = p1
    		x2,y2 = p2
    		x3,y3 = p3
    ''', file=file)
    		for name,value in defs:
    			print('		%s = %s' % (name, value), file=file)

    		print(file=file)
    		for name,value in zip([f[0] for f in funcs], exprs):
    			print('		self.%s += %s' % (name, value), file=file)

    	print('''
    if __name__ == '__main__':
    	from fontTools.misc.symfont import x, y, printGreenPen
    	printGreenPen('%s', ['''%penName, file=file)
    	for name,f in funcs:
    		print("		      ('%s', %s)," % (name, str(f)), file=file)
    	print('		     ])', file=file)

    if __name__ == '__main__':
    	pen = AreaPen()
    	pen.moveTo((100,100))
    	pen.lineTo((100,200))
    	pen.lineTo((200,200))
    	pen.curveTo((200,250),(300,300),(250,350))
    	pen.lineTo((200,100))
    	pen.closePath()
    	print(pen.value)