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