1 from __future__ import print_function, division, absolute_import 2 from fontTools.misc.py23 import * 3 from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect 4 from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds 5 from fontTools.pens.basePen import BasePen 6 7 8 __all__ = ["BoundsPen", "ControlBoundsPen"] 9 10 11 class 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 23 def __init__(self, glyphSet): 24 BasePen.__init__(self, glyphSet) 25 self.bounds = None 26 27 def _moveTo(self, pt): 28 bounds = self.bounds 29 if bounds: 30 self.bounds = updateBounds(bounds, pt) 31 else: 32 x, y = pt 33 self.bounds = (x, y, x, y) 34 35 def _lineTo(self, pt): 36 self.bounds = updateBounds(self.bounds, pt) 37 38 def _curveToOne(self, bcp1, bcp2, pt): 39 bounds = self.bounds 40 bounds = updateBounds(bounds, bcp1) 41 bounds = updateBounds(bounds, bcp2) 42 bounds = updateBounds(bounds, pt) 43 self.bounds = bounds 44 45 def _qCurveToOne(self, bcp, pt): 46 bounds = self.bounds 47 bounds = updateBounds(bounds, bcp) 48 bounds = updateBounds(bounds, pt) 49 self.bounds = bounds 50 51 52 class BoundsPen(ControlBoundsPen): 53 54 """Pen to calculate the bounds of a shape. It calculates the 55 correct bounds even when the shape contains curves that don't 56 have points on their extremes. This is somewhat slower to compute 57 than the "control bounds". 58 59 When the shape has been drawn, the bounds are available as the 60 'bounds' attribute of the pen object. It's a 4-tuple: 61 (xMin, yMin, xMax, yMax) 62 """ 63 64 def _curveToOne(self, bcp1, bcp2, pt): 65 bounds = self.bounds 66 bounds = updateBounds(bounds, pt) 67 if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): 68 bounds = unionRect(bounds, calcCubicBounds( 69 self._getCurrentPoint(), bcp1, bcp2, pt)) 70 self.bounds = bounds 71 72 def _qCurveToOne(self, bcp, pt): 73 bounds = self.bounds 74 bounds = updateBounds(bounds, pt) 75 if not pointInRect(bcp, bounds): 76 bounds = unionRect(bounds, calcQuadraticBounds( 77 self._getCurrentPoint(), bcp, pt)) 78 self.bounds = bounds 79 80 81 if __name__ == "__main__": 82 def draw(pen): 83 pen.moveTo((0, 0)) 84 pen.lineTo((0, 100)) 85 pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0)) 86 pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100)) 87 pen.closePath() 88 89 pen = ControlBoundsPen(None) 90 draw(pen) 91 print(pen.bounds) 92 93 pen = BoundsPen(None) 94 draw(pen) 95 print(pen.bounds) 96