11ae29591efbb29492ce05378909ccf4028d7c1eeBehdad Esfahbodfrom __future__ import print_function, division, absolute_import 230e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.misc.py23 import * 3b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrfrom fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect 4b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrfrom fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds 530e691edd056ba22fa8970280e986747817bec3dBehdad Esfahbodfrom fontTools.pens.basePen import BasePen 6b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 7b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 8b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr__all__ = ["BoundsPen", "ControlBoundsPen"] 9b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 10b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 11b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass ControlBoundsPen(BasePen): 12b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 13b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr """Pen to calculate the "control bounds" of a shape. This is the 14b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounding box of all control points, so may be larger than the 15b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr actual bounding box if there are curves that don't have points 16b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr on their extremes. 17b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 18b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr When the shape has been drawn, the bounds are available as the 19b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 'bounds' attribute of the pen object. It's a 4-tuple: 20b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr (xMin, yMin, xMax, yMax) 21b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr """ 22b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 23b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def __init__(self, glyphSet): 24b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr BasePen.__init__(self, glyphSet) 25b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = None 26b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 27b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _moveTo(self, pt): 28b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = self.bounds 29b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr if bounds: 30b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = updateBounds(bounds, pt) 31b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr else: 32b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr x, y = pt 33b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = (x, y, x, y) 34b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 35b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _lineTo(self, pt): 36b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = updateBounds(self.bounds, pt) 37b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 38b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _curveToOne(self, bcp1, bcp2, pt): 39b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = self.bounds 40b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, bcp1) 41b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, bcp2) 42b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, pt) 43b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = bounds 44b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 45b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _qCurveToOne(self, bcp, pt): 46b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = self.bounds 47b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, bcp) 48b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, pt) 49b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = bounds 50b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 51b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 52b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrclass BoundsPen(ControlBoundsPen): 53b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 54b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr """Pen to calculate the bounds of a shape. It calculates the 55b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr correct bounds even when the shape contains curves that don't 56b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr have points on their extremes. This is somewhat slower to compute 57b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr than the "control bounds". 58b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 59b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr When the shape has been drawn, the bounds are available as the 60b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 'bounds' attribute of the pen object. It's a 4-tuple: 61b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr (xMin, yMin, xMax, yMax) 62b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr """ 63b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 64b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _curveToOne(self, bcp1, bcp2, pt): 65b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = self.bounds 66b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, pt) 67b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): 6882ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr bounds = unionRect(bounds, calcCubicBounds( 6982ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr self._getCurrentPoint(), bcp1, bcp2, pt)) 70b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = bounds 71b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 72b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def _qCurveToOne(self, bcp, pt): 73b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = self.bounds 74b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr bounds = updateBounds(bounds, pt) 75b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr if not pointInRect(bcp, bounds): 7682ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr bounds = unionRect(bounds, calcQuadraticBounds( 7782ef2a52c9ab8a88f10daa9dc1050d1cf3901b57jvr self._getCurrentPoint(), bcp, pt)) 78b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr self.bounds = bounds 79b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 80b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 81b369ef33fcbfd39bd5aaf2b079cef1c689095783jvrif __name__ == "__main__": 82b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr def draw(pen): 83b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen.moveTo((0, 0)) 84b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen.lineTo((0, 100)) 85b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0)) 86b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100)) 87b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen.closePath() 88b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 89b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen = ControlBoundsPen(None) 90b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr draw(pen) 913ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print(pen.bounds) 92b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr 93b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr pen = BoundsPen(None) 94b369ef33fcbfd39bd5aaf2b079cef1c689095783jvr draw(pen) 953ec6a258238b6068e4eef3fe579f1f5c0a06bbbaBehdad Esfahbod print(pen.bounds) 96