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 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 52class 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 81if __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