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