boundsPen.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from fontTools.misc.arrayTools import updateBounds, pointInRect, unionRect
  2. from fontTools.misc.bezierTools import calcCubicBounds, calcQuadraticBounds
  3. from fontTools.pens.basePen import BasePen
  4. __all__ = ["BoundsPen", "ControlBoundsPen"]
  5. class ControlBoundsPen(BasePen):
  6. """Pen to calculate the "control bounds" of a shape. This is the
  7. bounding box of all control points, so may be larger than the
  8. actual bounding box if there are curves that don't have points
  9. on their extremes.
  10. When the shape has been drawn, the bounds are available as the
  11. ``bounds`` attribute of the pen object. It's a 4-tuple::
  12. (xMin, yMin, xMax, yMax).
  13. If ``ignoreSinglePoints`` is True, single points are ignored.
  14. """
  15. def __init__(self, glyphSet, ignoreSinglePoints=False):
  16. BasePen.__init__(self, glyphSet)
  17. self.ignoreSinglePoints = ignoreSinglePoints
  18. self.init()
  19. def init(self):
  20. self.bounds = None
  21. self._start = None
  22. def _moveTo(self, pt):
  23. self._start = pt
  24. if not self.ignoreSinglePoints:
  25. self._addMoveTo()
  26. def _addMoveTo(self):
  27. if self._start is None:
  28. return
  29. bounds = self.bounds
  30. if bounds:
  31. self.bounds = updateBounds(bounds, self._start)
  32. else:
  33. x, y = self._start
  34. self.bounds = (x, y, x, y)
  35. self._start = None
  36. def _lineTo(self, pt):
  37. self._addMoveTo()
  38. self.bounds = updateBounds(self.bounds, pt)
  39. def _curveToOne(self, bcp1, bcp2, pt):
  40. self._addMoveTo()
  41. bounds = self.bounds
  42. bounds = updateBounds(bounds, bcp1)
  43. bounds = updateBounds(bounds, bcp2)
  44. bounds = updateBounds(bounds, pt)
  45. self.bounds = bounds
  46. def _qCurveToOne(self, bcp, pt):
  47. self._addMoveTo()
  48. bounds = self.bounds
  49. bounds = updateBounds(bounds, bcp)
  50. bounds = updateBounds(bounds, pt)
  51. self.bounds = bounds
  52. class BoundsPen(ControlBoundsPen):
  53. """Pen to calculate the bounds of a shape. It calculates the
  54. correct bounds even when the shape contains curves that don't
  55. have points on their extremes. This is somewhat slower to compute
  56. than the "control bounds".
  57. When the shape has been drawn, the bounds are available as the
  58. ``bounds`` attribute of the pen object. It's a 4-tuple::
  59. (xMin, yMin, xMax, yMax)
  60. """
  61. def _curveToOne(self, bcp1, bcp2, pt):
  62. self._addMoveTo()
  63. bounds = self.bounds
  64. bounds = updateBounds(bounds, pt)
  65. if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds):
  66. bounds = unionRect(
  67. bounds, calcCubicBounds(self._getCurrentPoint(), bcp1, bcp2, pt)
  68. )
  69. self.bounds = bounds
  70. def _qCurveToOne(self, bcp, pt):
  71. self._addMoveTo()
  72. bounds = self.bounds
  73. bounds = updateBounds(bounds, pt)
  74. if not pointInRect(bcp, bounds):
  75. bounds = unionRect(
  76. bounds, calcQuadraticBounds(self._getCurrentPoint(), bcp, pt)
  77. )
  78. self.bounds = bounds