| from __future__ import print_function, division, absolute_import |
| from fontTools.misc.py23 import * |
| from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect |
| from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds |
| from fontTools.pens.basePen import BasePen |
| |
| |
| __all__ = ["BoundsPen", "ControlBoundsPen"] |
| |
| |
| class ControlBoundsPen(BasePen): |
| |
| """Pen to calculate the "control bounds" of a shape. This is the |
| bounding box of all control points, so may be larger than the |
| actual bounding box if there are curves that don't have points |
| on their extremes. |
| |
| When the shape has been drawn, the bounds are available as the |
| 'bounds' attribute of the pen object. It's a 4-tuple: |
| (xMin, yMin, xMax, yMax) |
| """ |
| |
| def __init__(self, glyphSet): |
| BasePen.__init__(self, glyphSet) |
| self.bounds = None |
| |
| def _moveTo(self, pt): |
| bounds = self.bounds |
| if bounds: |
| self.bounds = updateBounds(bounds, pt) |
| else: |
| x, y = pt |
| self.bounds = (x, y, x, y) |
| |
| def _lineTo(self, pt): |
| self.bounds = updateBounds(self.bounds, pt) |
| |
| def _curveToOne(self, bcp1, bcp2, pt): |
| bounds = self.bounds |
| bounds = updateBounds(bounds, bcp1) |
| bounds = updateBounds(bounds, bcp2) |
| bounds = updateBounds(bounds, pt) |
| self.bounds = bounds |
| |
| def _qCurveToOne(self, bcp, pt): |
| bounds = self.bounds |
| bounds = updateBounds(bounds, bcp) |
| bounds = updateBounds(bounds, pt) |
| self.bounds = bounds |
| |
| |
| class BoundsPen(ControlBoundsPen): |
| |
| """Pen to calculate the bounds of a shape. It calculates the |
| correct bounds even when the shape contains curves that don't |
| have points on their extremes. This is somewhat slower to compute |
| than the "control bounds". |
| |
| When the shape has been drawn, the bounds are available as the |
| 'bounds' attribute of the pen object. It's a 4-tuple: |
| (xMin, yMin, xMax, yMax) |
| """ |
| |
| def _curveToOne(self, bcp1, bcp2, pt): |
| bounds = self.bounds |
| bounds = updateBounds(bounds, pt) |
| if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): |
| bounds = unionRect(bounds, calcCubicBounds( |
| self._getCurrentPoint(), bcp1, bcp2, pt)) |
| self.bounds = bounds |
| |
| def _qCurveToOne(self, bcp, pt): |
| bounds = self.bounds |
| bounds = updateBounds(bounds, pt) |
| if not pointInRect(bcp, bounds): |
| bounds = unionRect(bounds, calcQuadraticBounds( |
| self._getCurrentPoint(), bcp, pt)) |
| self.bounds = bounds |
| |
| |
| if __name__ == "__main__": |
| def draw(pen): |
| pen.moveTo((0, 0)) |
| pen.lineTo((0, 100)) |
| pen.qCurveTo((50, 75), (60, 50), (50, 25), (0, 0)) |
| pen.curveTo((-50, 25), (-60, 50), (-50, 75), (0, 100)) |
| pen.closePath() |
| |
| pen = ControlBoundsPen(None) |
| draw(pen) |
| print(pen.bounds) |
| |
| pen = BoundsPen(None) |
| draw(pen) |
| print(pen.bounds) |