1*e1fe3e4aSElliott Hughesfrom fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect 2*e1fe3e4aSElliott Hughesfrom fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds 3*e1fe3e4aSElliott Hughesfrom fontTools.pens.basePen import BasePen 4*e1fe3e4aSElliott Hughes 5*e1fe3e4aSElliott Hughes 6*e1fe3e4aSElliott Hughes__all__ = ["BoundsPen", "ControlBoundsPen"] 7*e1fe3e4aSElliott Hughes 8*e1fe3e4aSElliott Hughes 9*e1fe3e4aSElliott Hughesclass ControlBoundsPen(BasePen): 10*e1fe3e4aSElliott Hughes """Pen to calculate the "control bounds" of a shape. This is the 11*e1fe3e4aSElliott Hughes bounding box of all control points, so may be larger than the 12*e1fe3e4aSElliott Hughes actual bounding box if there are curves that don't have points 13*e1fe3e4aSElliott Hughes on their extremes. 14*e1fe3e4aSElliott Hughes 15*e1fe3e4aSElliott Hughes When the shape has been drawn, the bounds are available as the 16*e1fe3e4aSElliott Hughes ``bounds`` attribute of the pen object. It's a 4-tuple:: 17*e1fe3e4aSElliott Hughes 18*e1fe3e4aSElliott Hughes (xMin, yMin, xMax, yMax). 19*e1fe3e4aSElliott Hughes 20*e1fe3e4aSElliott Hughes If ``ignoreSinglePoints`` is True, single points are ignored. 21*e1fe3e4aSElliott Hughes """ 22*e1fe3e4aSElliott Hughes 23*e1fe3e4aSElliott Hughes def __init__(self, glyphSet, ignoreSinglePoints=False): 24*e1fe3e4aSElliott Hughes BasePen.__init__(self, glyphSet) 25*e1fe3e4aSElliott Hughes self.ignoreSinglePoints = ignoreSinglePoints 26*e1fe3e4aSElliott Hughes self.init() 27*e1fe3e4aSElliott Hughes 28*e1fe3e4aSElliott Hughes def init(self): 29*e1fe3e4aSElliott Hughes self.bounds = None 30*e1fe3e4aSElliott Hughes self._start = None 31*e1fe3e4aSElliott Hughes 32*e1fe3e4aSElliott Hughes def _moveTo(self, pt): 33*e1fe3e4aSElliott Hughes self._start = pt 34*e1fe3e4aSElliott Hughes if not self.ignoreSinglePoints: 35*e1fe3e4aSElliott Hughes self._addMoveTo() 36*e1fe3e4aSElliott Hughes 37*e1fe3e4aSElliott Hughes def _addMoveTo(self): 38*e1fe3e4aSElliott Hughes if self._start is None: 39*e1fe3e4aSElliott Hughes return 40*e1fe3e4aSElliott Hughes bounds = self.bounds 41*e1fe3e4aSElliott Hughes if bounds: 42*e1fe3e4aSElliott Hughes self.bounds = updateBounds(bounds, self._start) 43*e1fe3e4aSElliott Hughes else: 44*e1fe3e4aSElliott Hughes x, y = self._start 45*e1fe3e4aSElliott Hughes self.bounds = (x, y, x, y) 46*e1fe3e4aSElliott Hughes self._start = None 47*e1fe3e4aSElliott Hughes 48*e1fe3e4aSElliott Hughes def _lineTo(self, pt): 49*e1fe3e4aSElliott Hughes self._addMoveTo() 50*e1fe3e4aSElliott Hughes self.bounds = updateBounds(self.bounds, pt) 51*e1fe3e4aSElliott Hughes 52*e1fe3e4aSElliott Hughes def _curveToOne(self, bcp1, bcp2, pt): 53*e1fe3e4aSElliott Hughes self._addMoveTo() 54*e1fe3e4aSElliott Hughes bounds = self.bounds 55*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, bcp1) 56*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, bcp2) 57*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, pt) 58*e1fe3e4aSElliott Hughes self.bounds = bounds 59*e1fe3e4aSElliott Hughes 60*e1fe3e4aSElliott Hughes def _qCurveToOne(self, bcp, pt): 61*e1fe3e4aSElliott Hughes self._addMoveTo() 62*e1fe3e4aSElliott Hughes bounds = self.bounds 63*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, bcp) 64*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, pt) 65*e1fe3e4aSElliott Hughes self.bounds = bounds 66*e1fe3e4aSElliott Hughes 67*e1fe3e4aSElliott Hughes 68*e1fe3e4aSElliott Hughesclass BoundsPen(ControlBoundsPen): 69*e1fe3e4aSElliott Hughes """Pen to calculate the bounds of a shape. It calculates the 70*e1fe3e4aSElliott Hughes correct bounds even when the shape contains curves that don't 71*e1fe3e4aSElliott Hughes have points on their extremes. This is somewhat slower to compute 72*e1fe3e4aSElliott Hughes than the "control bounds". 73*e1fe3e4aSElliott Hughes 74*e1fe3e4aSElliott Hughes When the shape has been drawn, the bounds are available as the 75*e1fe3e4aSElliott Hughes ``bounds`` attribute of the pen object. It's a 4-tuple:: 76*e1fe3e4aSElliott Hughes 77*e1fe3e4aSElliott Hughes (xMin, yMin, xMax, yMax) 78*e1fe3e4aSElliott Hughes """ 79*e1fe3e4aSElliott Hughes 80*e1fe3e4aSElliott Hughes def _curveToOne(self, bcp1, bcp2, pt): 81*e1fe3e4aSElliott Hughes self._addMoveTo() 82*e1fe3e4aSElliott Hughes bounds = self.bounds 83*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, pt) 84*e1fe3e4aSElliott Hughes if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): 85*e1fe3e4aSElliott Hughes bounds = unionRect( 86*e1fe3e4aSElliott Hughes bounds, calcCubicBounds(self._getCurrentPoint(), bcp1, bcp2, pt) 87*e1fe3e4aSElliott Hughes ) 88*e1fe3e4aSElliott Hughes self.bounds = bounds 89*e1fe3e4aSElliott Hughes 90*e1fe3e4aSElliott Hughes def _qCurveToOne(self, bcp, pt): 91*e1fe3e4aSElliott Hughes self._addMoveTo() 92*e1fe3e4aSElliott Hughes bounds = self.bounds 93*e1fe3e4aSElliott Hughes bounds = updateBounds(bounds, pt) 94*e1fe3e4aSElliott Hughes if not pointInRect(bcp, bounds): 95*e1fe3e4aSElliott Hughes bounds = unionRect( 96*e1fe3e4aSElliott Hughes bounds, calcQuadraticBounds(self._getCurrentPoint(), bcp, pt) 97*e1fe3e4aSElliott Hughes ) 98*e1fe3e4aSElliott Hughes self.bounds = bounds 99