I used this in the following script, which rounds coordinates of elements inside glyph using my own method. It works on selected elements. I put snap to zero in RF preferences so I get floating-point coordinates. So I run this when I want to finish the work:
from fontTools.pens.t2CharStringPen import t2c_round
from fontTools.misc.fixedTools import otRound
TOLERANCE = 0.5
def roundElement(e, tolerance=TOLERANCE):
old_x, old_y = e.x, e.y
e.x = t2c_round(e.x, tolerance)
e.y = t2c_round(e.y, tolerance)
if old_x != e.x or old_y != e.y:
return True
return False
def scaleRound(value):
"""
used to round component scales to 2 decimal points.
"""
return round(value, 2)
def getSelectedPoints(glyph):
pts = []
for contour in glyph.contours:
for i, segment in enumerate(contour.segments):
for pt in segment:
if not pt.selected:
continue
pts.append(pt)
# implicit == include BCPs in selection
if pt.type != 'offcurve':
# bcpIn
if len(segment) == 3:
bcpIn = segment[-2]
pts.append(bcpIn)
# bcpOut
try:
nextSegment = contour[i + 1]
except IndexError:
nextSegment = contour[0]
if len(nextSegment) == 3:
bcpOut = nextSegment[0]
pts.append(bcpOut)
return pts
def roundGlyph(g, markGlyph=True):
"""
Round selected objects coordinates in a given glyph.
If nothing is selected, round all the elements insde glyph.
"""
g.prepareUndo("Round coordinates to integer")
if g.contours != () or g.anchors != () or g.components != ():
fixedState = set()
selectedPoints = g.selectedPoints
selectedAnchors = g.selectedAnchors
selectedComponents = g.selectedComponents
hasSelection = True in [selection != () for selection in [selectedPoints, selectedAnchors, selectedComponents]]
if hasSelection:
fixedState.update(map(roundElement, getSelectedPoints(g)))
else:
for c in g.contours:
fixedState.update(map(roundElement, c.points))
if True in fixedState:
fixedState.clear()
fixedState.add("Points")
else:
fixedState.clear()
for c in g.components:
if hasSelection and c not in selectedComponents:
continue
xScale, xyScale, yxScale, yScale, xOffset, yOffset = c.transformation
new_transformation = (scaleRound(xScale), scaleRound(xyScale), scaleRound(yxScale), scaleRound(yScale), otRound(xOffset), otRound(yOffset))
if new_transformation != c.transformation:
c.transformation = new_transformation
fixedState.add("Components")
for a in g.anchors:
if hasSelection and a not in selectedAnchors:
continue
if roundElement(a) is True:
fixedState.add("Anchros")
g_width = g.width
g_width_rounded = otRound(g_width)
if g_width != g_width_rounded:
g.width = g_width_rounded
fixedState.add("Width")
if fixedState != set():
print('%s fixed: %s' % (g.name, "\n".join(sorted(fixedState))))
if markGlyph is True:
g.markColor = (0.1, 0.8, 0.2, 1)
g.changed()
g.performUndo()
if __name__ == '__main__':
from mojo.UI import *
OutputWindow().clear()
f = CurrentFont()
cg = CurrentGlyph()
glist = []
if len(f.templateSelectedGlyphNames) < 2 and cg is not None:
roundGlyph(cg, False)
else:
for gl in f.selectedGlyphNames:
g = f[gl]
roundGlyph(g)