1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
from fontTools.pens.basePen import AbstractPen, BasePen
from robofab.misc.bezierTools import splitLine, splitCubic
from sets import Set
class MarginPen(BasePen):
"""
Pen to calculate the margins at a given value.
When isHorizontal is True, the margins at <value> are horizontal.
When isHorizontal is False, the margins at <value> are vertical.
When a glyphset or font is given, MarginPen will also calculate for glyphs with components.
pen.getMargins() returns the minimum and maximum intersections of the glyph.
pen.getContourMargins() returns the minimum and maximum intersections for each contour.
Possible optimisation:
Initialise the pen object with a list of points we want to measure,
then draw the glyph once, but do the splitLine() math for all measure points.
"""
def __init__(self, glyphSet, value, isHorizontal=True):
BasePen.__init__(self, glyphSet)
self.value = value
self.hits = {}
self.filterDoubles = True
self.contourIndex = None
self.startPt = None
self.isHorizontal = isHorizontal
def _moveTo(self, pt):
self.currentPt = pt
self.startPt = pt
if self.contourIndex is None:
self.contourIndex = 0
else:
self.contourIndex += 1
def _lineTo(self, pt):
if self.filterDoubles:
if pt == self.currentPt:
return
hits = splitLine(self.currentPt, pt, self.value, self.isHorizontal)
if len(hits)>1:
# result will be 2 tuples of 2 coordinates
# first two points: start to intersect
# second two points: intersect to end
# so, second point in first tuple is the intersect
# then, the first coordinate of that point is the x.
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
if self.isHorizontal:
self.hits[self.contourIndex].append(round(hits[0][-1][0], 4))
else:
self.hits[self.contourIndex].append(round(hits[0][-1][1], 4))
if self.isHorizontal and pt[1] == self.value:
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt[0])
elif (not self.isHorizontal) and (pt[0] == self.value):
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt[1])
self.currentPt = pt
def _curveToOne(self, pt1, pt2, pt3):
hits = splitCubic(self.currentPt, pt1, pt2, pt3, self.value, self.isHorizontal)
for i in range(len(hits)-1):
# a number of intersections is possible. Just take the
# last point of each segment.
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
if self.isHorizontal:
self.hits[self.contourIndex].append(round(hits[i][-1][0], 4))
else:
self.hits[self.contourIndex].append(round(hits[i][-1][1], 4))
if self.isHorizontal and pt3[1] == self.value:
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt3[0])
if (not self.isHorizontal) and (pt3[0] == self.value):
# it could happen
if not self.contourIndex in self.hits:
self.hits[self.contourIndex] = []
self.hits[self.contourIndex].append(pt3[1])
self.currentPt = pt3
def _closePath(self):
if self.currentPt != self.startPt:
self._lineTo(self.startPt)
self.currentPt = self.startPt = None
def _endPath(self):
self.currentPt = None
def addComponent(self, baseGlyph, transformation):
from fontTools.pens.transformPen import TransformPen
if self.glyphSet is None:
return
if baseGlyph in self.glyphSet:
glyph = self.glyphSet[baseGlyph]
if glyph is None:
return
tPen = TransformPen(self, transformation)
glyph.draw(tPen)
def getMargins(self):
"""Get the horizontal margins for all contours combined, i.e. the whole glyph."""
allHits = []
for index, pts in self.hits.items():
allHits.extend(pts)
if allHits:
return min(allHits), max(allHits)
return None
def getContourMargins(self):
"""Get the horizontal margins for each contour."""
allHits = {}
for index, pts in self.hits.items():
unique = list(Set(pts))
unique.sort()
allHits[index] = unique
return allHits
def getAll(self):
"""Get all the slices."""
allHits = []
for index, pts in self.hits.items():
allHits.extend(pts)
unique = list(Set(allHits))
unique = list(unique)
unique.sort()
return unique
if __name__ == "__main__":
from robofab.world import CurrentGlyph, CurrentFont
f = CurrentFont()
g = CurrentGlyph()
pt = (74, 216)
pen = MarginPen(f, pt[1], isHorizontal=False)
g.draw(pen)
print 'glyph Y margins', pen.getMargins()
print pen.getContourMargins()
|