mathGlyph -> PointToSegmentPen -> drawPoints -> pointPen -> beginPath gets an ‘identifier’ argument from the ufo3 drawPoints. Which trips an error. This alleviates the problem but does not address anything that beginPath should be doing with the identifier.
173 lines
5.2 KiB
Python
173 lines
5.2 KiB
Python
from fontTools.pens.basePen import AbstractPen
|
|
from ufoLib.pointPen import AbstractPointPen
|
|
|
|
__all__ = ["AbstractPointPen", "BasePointToSegmentPen", "PrintingPointPen",
|
|
"PrintingSegmentPen", "SegmentPrintingPointPen"]
|
|
|
|
|
|
class BasePointToSegmentPen(AbstractPointPen):
|
|
|
|
"""Base class for retrieving the outline in a segment-oriented
|
|
way. The PointPen protocol is simple yet also a little tricky,
|
|
so when you need an outline presented as segments but you have
|
|
as points, do use this base implementation as it properly takes
|
|
care of all the edge cases.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.currentPath = None
|
|
|
|
def beginPath(self, **kwargs):
|
|
assert self.currentPath is None
|
|
self.currentPath = []
|
|
|
|
def _flushContour(self, segments):
|
|
"""Override this method.
|
|
|
|
It will be called for each non-empty sub path with a list
|
|
of segments: the 'segments' argument.
|
|
|
|
The segments list contains tuples of length 2:
|
|
(segmentType, points)
|
|
|
|
segmentType is one of "move", "line", "curve" or "qcurve".
|
|
"move" may only occur as the first segment, and it signifies
|
|
an OPEN path. A CLOSED path does NOT start with a "move", in
|
|
fact it will not contain a "move" at ALL.
|
|
|
|
The 'points' field in the 2-tuple is a list of point info
|
|
tuples. The list has 1 or more items, a point tuple has
|
|
four items:
|
|
(point, smooth, name, kwargs)
|
|
'point' is an (x, y) coordinate pair.
|
|
|
|
For a closed path, the initial moveTo point is defined as
|
|
the last point of the last segment.
|
|
|
|
The 'points' list of "move" and "line" segments always contains
|
|
exactly one point tuple.
|
|
"""
|
|
raise NotImplementedError
|
|
|
|
def endPath(self):
|
|
assert self.currentPath is not None
|
|
points = self.currentPath
|
|
self.currentPath = None
|
|
if not points:
|
|
return
|
|
if len(points) == 1:
|
|
# Not much more we can do than output a single move segment.
|
|
pt, segmentType, smooth, name, kwargs = points[0]
|
|
segments = [("move", [(pt, smooth, name, kwargs)])]
|
|
self._flushContour(segments)
|
|
return
|
|
segments = []
|
|
if points[0][1] == "move":
|
|
# It's an open contour, insert a "move" segment for the first
|
|
# point and remove that first point from the point list.
|
|
pt, segmentType, smooth, name, kwargs = points[0]
|
|
segments.append(("move", [(pt, smooth, name, kwargs)]))
|
|
points.pop(0)
|
|
else:
|
|
# It's a closed contour. Locate the first on-curve point, and
|
|
# rotate the point list so that it _ends_ with an on-curve
|
|
# point.
|
|
firstOnCurve = None
|
|
for i in range(len(points)):
|
|
segmentType = points[i][1]
|
|
if segmentType is not None:
|
|
firstOnCurve = i
|
|
break
|
|
if firstOnCurve is None:
|
|
# Special case for quadratics: a contour with no on-curve
|
|
# points. Add a "None" point. (See also the Pen protocol's
|
|
# qCurveTo() method and fontTools.pens.basePen.py.)
|
|
points.append((None, "qcurve", None, None, None))
|
|
else:
|
|
points = points[firstOnCurve+1:] + points[:firstOnCurve+1]
|
|
|
|
currentSegment = []
|
|
for pt, segmentType, smooth, name, kwargs in points:
|
|
currentSegment.append((pt, smooth, name, kwargs))
|
|
if segmentType is None:
|
|
continue
|
|
segments.append((segmentType, currentSegment))
|
|
currentSegment = []
|
|
|
|
self._flushContour(segments)
|
|
|
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
|
|
self.currentPath.append((pt, segmentType, smooth, name, kwargs))
|
|
|
|
|
|
class PrintingPointPen(AbstractPointPen):
|
|
|
|
def __init__(self):
|
|
self.havePath = False
|
|
|
|
def beginPath(self, identifier=None, **kwargs):
|
|
self.havePath = True
|
|
args = []
|
|
if identifier is not None:
|
|
args.append("identifier=%r" % identifier)
|
|
if kwargs:
|
|
args.append("**%s" % kwargs)
|
|
print "pen.beginPath(%s)" % ", ".join(args)
|
|
|
|
def endPath(self):
|
|
self.havePath = False
|
|
print "pen.endPath()"
|
|
|
|
def addPoint(self, pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs):
|
|
assert self.havePath
|
|
args = ["(%s, %s)" % (pt[0], pt[1])]
|
|
if segmentType is not None:
|
|
args.append("segmentType=%r" % segmentType)
|
|
if smooth:
|
|
args.append("smooth=True")
|
|
if name is not None:
|
|
args.append("name=%r" % name)
|
|
if identifier is not None:
|
|
args.append("identifier=%r" % identifier)
|
|
if kwargs:
|
|
args.append("**%s" % kwargs)
|
|
print "pen.addPoint(%s)" % ", ".join(args)
|
|
|
|
def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
|
|
assert not self.havePath
|
|
args = [baseGlyphName, str(tuple(transformation))]
|
|
if identifier is not None:
|
|
args.append("identifier=%r" % identifier)
|
|
if kwargs:
|
|
args.append("**%s" % kwargs)
|
|
print "pen.addComponent(%s)" % ", ".join(args)
|
|
|
|
|
|
class PrintingSegmentPen(AbstractPen):
|
|
def moveTo(self, pt):
|
|
print "pen.moveTo(%s)" % (pt,)
|
|
def lineTo(self, pt):
|
|
print "pen.lineTo(%s)" % (pt,)
|
|
def curveTo(self, *pts):
|
|
print "pen.curveTo%s" % (pts,)
|
|
def qCurveTo(self, *pts):
|
|
print "pen.qCurveTo%s" % (pts,)
|
|
def closePath(self):
|
|
print "pen.closePath()"
|
|
def endPath(self):
|
|
print "pen.endPath()"
|
|
def addComponent(self, baseGlyphName, transformation):
|
|
print "pen.addComponent(%r, %s)" % (baseGlyphName, tuple(transformation))
|
|
|
|
|
|
class SegmentPrintingPointPen(BasePointToSegmentPen):
|
|
def _flushContour(self, segments):
|
|
from pprint import pprint
|
|
pprint(segments)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
p = SegmentPrintingPointPen()
|
|
from robofab.test.test_pens import TestShapes
|
|
TestShapes.onCurveLessQuadShape(p)
|