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). | |

If ``ignoreSinglePoints`` is True, single points are ignored. | |

""" | |

def __init__(self, glyphSet, ignoreSinglePoints=False): | |

BasePen.__init__(self, glyphSet) | |

self.ignoreSinglePoints = ignoreSinglePoints | |

self.init() | |

def init(self): | |

self.bounds = None | |

self._start = None | |

def _moveTo(self, pt): | |

self._start = pt | |

if not self.ignoreSinglePoints: | |

self._addMoveTo() | |

def _addMoveTo(self): | |

if self._start is None: | |

return | |

bounds = self.bounds | |

if bounds: | |

self.bounds = updateBounds(bounds, self._start) | |

else: | |

x, y = self._start | |

self.bounds = (x, y, x, y) | |

self._start = None | |

def _lineTo(self, pt): | |

self._addMoveTo() | |

self.bounds = updateBounds(self.bounds, pt) | |

def _curveToOne(self, bcp1, bcp2, pt): | |

self._addMoveTo() | |

bounds = self.bounds | |

bounds = updateBounds(bounds, bcp1) | |

bounds = updateBounds(bounds, bcp2) | |

bounds = updateBounds(bounds, pt) | |

self.bounds = bounds | |

def _qCurveToOne(self, bcp, pt): | |

self._addMoveTo() | |

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): | |

self._addMoveTo() | |

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): | |

self._addMoveTo() | |

bounds = self.bounds | |

bounds = updateBounds(bounds, pt) | |

if not pointInRect(bcp, bounds): | |

bounds = unionRect(bounds, calcQuadraticBounds( | |

self._getCurrentPoint(), bcp, pt)) | |

self.bounds = bounds |