1# -*- coding: utf-8 -*- 2"""Calculate the perimeter of a glyph.""" 3 4from fontTools.pens.basePen import BasePen 5from fontTools.misc.bezierTools import approximateQuadraticArcLengthC, calcQuadraticArcLengthC, approximateCubicArcLengthC, calcCubicArcLengthC 6import math 7 8 9__all__ = ["PerimeterPen"] 10 11 12def _distance(p0, p1): 13 return math.hypot(p0[0] - p1[0], p0[1] - p1[1]) 14 15class PerimeterPen(BasePen): 16 17 def __init__(self, glyphset=None, tolerance=0.005): 18 BasePen.__init__(self, glyphset) 19 self.value = 0 20 self.tolerance = tolerance 21 22 # Choose which algorithm to use for quadratic and for cubic. 23 # Quadrature is faster but has fixed error characteristic with no strong 24 # error bound. The cutoff points are derived empirically. 25 self._addCubic = self._addCubicQuadrature if tolerance >= 0.0015 else self._addCubicRecursive 26 self._addQuadratic = self._addQuadraticQuadrature if tolerance >= 0.00075 else self._addQuadraticExact 27 28 def _moveTo(self, p0): 29 self.__startPoint = p0 30 31 def _closePath(self): 32 p0 = self._getCurrentPoint() 33 if p0 != self.__startPoint: 34 self._lineTo(self.__startPoint) 35 36 def _lineTo(self, p1): 37 p0 = self._getCurrentPoint() 38 self.value += _distance(p0, p1) 39 40 def _addQuadraticExact(self, c0, c1, c2): 41 self.value += calcQuadraticArcLengthC(c0, c1, c2) 42 43 def _addQuadraticQuadrature(self, c0, c1, c2): 44 self.value += approximateQuadraticArcLengthC(c0, c1, c2) 45 46 def _qCurveToOne(self, p1, p2): 47 p0 = self._getCurrentPoint() 48 self._addQuadratic(complex(*p0), complex(*p1), complex(*p2)) 49 50 def _addCubicRecursive(self, c0, c1, c2, c3): 51 self.value += calcCubicArcLengthC(c0, c1, c2, c3, self.tolerance) 52 53 def _addCubicQuadrature(self, c0, c1, c2, c3): 54 self.value += approximateCubicArcLengthC(c0, c1, c2, c3) 55 56 def _curveToOne(self, p1, p2, p3): 57 p0 = self._getCurrentPoint() 58 self._addCubic(complex(*p0), complex(*p1), complex(*p2), complex(*p3)) 59