diff options
author | Rasmus Andersson <rasmus@notion.se> | 2018-09-03 22:55:49 +0300 |
---|---|---|
committer | Rasmus Andersson <rasmus@notion.se> | 2018-09-03 22:55:49 +0300 |
commit | c833e252c925e8dd68108660710ca835d95daa6f (patch) | |
tree | 6b2e28264ed45efd7f054e453b622098d0d875b8 /misc/pylib/robofab/pens | |
parent | 8c1a4c181ef12000179dfec541f1af87e9b03122 (diff) | |
download | inter-c833e252c925e8dd68108660710ca835d95daa6f.tar.xz |
Major overhaul, moving from UFO2 to Glyphs and UFO3, plus a brand new and much simpler fontbuild
Diffstat (limited to 'misc/pylib/robofab/pens')
-rwxr-xr-x | misc/pylib/robofab/pens/__init__.py | 11 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/adapterPens.py | 293 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/angledMarginPen.py | 132 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/boundsPen.pyx | 95 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/digestPen.py | 106 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/filterPen.py | 407 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/flPen.py | 274 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/marginPen.py | 155 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/mathPens.py | 185 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/pointPen.py | 173 | ||||
-rw-r--r-- | misc/pylib/robofab/pens/quartzPen.py | 21 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/reverseContourPointPen.py | 125 | ||||
-rwxr-xr-x | misc/pylib/robofab/pens/rfUFOPen.pyx | 103 |
13 files changed, 0 insertions, 2080 deletions
diff --git a/misc/pylib/robofab/pens/__init__.py b/misc/pylib/robofab/pens/__init__.py deleted file mode 100755 index a5afc0dc3..000000000 --- a/misc/pylib/robofab/pens/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -""" - -Directory for all pen modules. -If you make a pen, put it here so that we can keep track of it. - - -""" - - - - diff --git a/misc/pylib/robofab/pens/adapterPens.py b/misc/pylib/robofab/pens/adapterPens.py deleted file mode 100644 index 0cd9ae3f7..000000000 --- a/misc/pylib/robofab/pens/adapterPens.py +++ /dev/null @@ -1,293 +0,0 @@ -import math -from fontTools.pens.basePen import AbstractPen -from robofab.pens.pointPen import AbstractPointPen, BasePointToSegmentPen - - -class FabToFontToolsPenAdapter: - - """Class that covers up the subtle differences between RoboFab - Pens and FontTools Pens. 'Fab should eventually move to FontTools - Pens, this class may help to make the transition smoother. - """ - - # XXX The change to FontTools pens has almost been completed. Any - # usage of this class should be highly suspect. - - def __init__(self, fontToolsPen): - self.fontToolsPen = fontToolsPen - - def moveTo(self, pt, **kargs): - self.fontToolsPen.moveTo(pt) - - def lineTo(self, pt, **kargs): - self.fontToolsPen.lineTo(pt) - - def curveTo(self, *pts, **kargs): - self.fontToolsPen.curveTo(*pts) - - def qCurveTo(self, *pts, **kargs): - self.fontToolsPen.qCurveTo(*pts) - - def closePath(self): - self.fontToolsPen.closePath() - - def endPath(self): - self.fontToolsPen.endPath() - - def addComponent(self, glyphName, offset=(0, 0), scale=(1, 1)): - self.fontToolsPen.addComponent(glyphName, - (scale[0], 0, 0, scale[1], offset[0], offset[1])) - - def setWidth(self, width): - self.width = width - - def setNote(self, note): - pass - - def addAnchor(self, name, pt): - self.fontToolsPen.moveTo(pt) - self.fontToolsPen.endPath() - - def doneDrawing(self): - pass - - -class PointToSegmentPen(BasePointToSegmentPen): - - """Adapter class that converts the PointPen protocol to the - (Segment)Pen protocol. - """ - - def __init__(self, segmentPen, outputImpliedClosingLine=False): - BasePointToSegmentPen.__init__(self) - self.pen = segmentPen - self.outputImpliedClosingLine = outputImpliedClosingLine - - def _flushContour(self, segments): - assert len(segments) >= 1 - pen = self.pen - if segments[0][0] == "move": - # It's an open path. - closed = False - points = segments[0][1] - assert len(points) == 1 - movePt, smooth, name, kwargs = points[0] - del segments[0] - else: - # It's a closed path, do a moveTo to the last - # point of the last segment. - closed = True - segmentType, points = segments[-1] - movePt, smooth, name, kwargs = points[-1] - if movePt is None: - # quad special case: a contour with no on-curve points contains - # one "qcurve" segment that ends with a point that's None. We - # must not output a moveTo() in that case. - pass - else: - pen.moveTo(movePt) - outputImpliedClosingLine = self.outputImpliedClosingLine - nSegments = len(segments) - for i in range(nSegments): - segmentType, points = segments[i] - points = [pt for pt, smooth, name, kwargs in points] - if segmentType == "line": - assert len(points) == 1 - pt = points[0] - if i + 1 != nSegments or outputImpliedClosingLine or not closed: - pen.lineTo(pt) - elif segmentType == "curve": - pen.curveTo(*points) - elif segmentType == "qcurve": - pen.qCurveTo(*points) - else: - assert 0, "illegal segmentType: %s" % segmentType - if closed: - pen.closePath() - else: - pen.endPath() - - def addComponent(self, glyphName, transform): - self.pen.addComponent(glyphName, transform) - - -class SegmentToPointPen(AbstractPen): - - """Adapter class that converts the (Segment)Pen protocol to the - PointPen protocol. - """ - - def __init__(self, pointPen, guessSmooth=True): - if guessSmooth: - self.pen = GuessSmoothPointPen(pointPen) - else: - self.pen = pointPen - self.contour = None - - def _flushContour(self): - pen = self.pen - pen.beginPath() - for pt, segmentType in self.contour: - pen.addPoint(pt, segmentType=segmentType) - pen.endPath() - - def moveTo(self, pt): - self.contour = [] - self.contour.append((pt, "move")) - - def lineTo(self, pt): - self.contour.append((pt, "line")) - - def curveTo(self, *pts): - for pt in pts[:-1]: - self.contour.append((pt, None)) - self.contour.append((pts[-1], "curve")) - - def qCurveTo(self, *pts): - if pts[-1] is None: - self.contour = [] - for pt in pts[:-1]: - self.contour.append((pt, None)) - if pts[-1] is not None: - self.contour.append((pts[-1], "qcurve")) - - def closePath(self): - if len(self.contour) > 1 and self.contour[0][0] == self.contour[-1][0]: - self.contour[0] = self.contour[-1] - del self.contour[-1] - else: - # There's an implied line at the end, replace "move" with "line" - # for the first point - pt, tp = self.contour[0] - if tp == "move": - self.contour[0] = pt, "line" - self._flushContour() - self.contour = None - - def endPath(self): - self._flushContour() - self.contour = None - - def addComponent(self, glyphName, transform): - assert self.contour is None - self.pen.addComponent(glyphName, transform) - - -class TransformPointPen(AbstractPointPen): - - """PointPen that transforms all coordinates, and passes them to another - PointPen. It also transforms the transformation given to addComponent(). - """ - - def __init__(self, outPen, transformation): - if not hasattr(transformation, "transformPoint"): - from fontTools.misc.transform import Transform - transformation = Transform(*transformation) - self._transformation = transformation - self._transformPoint = transformation.transformPoint - self._outPen = outPen - self._stack = [] - - def beginPath(self): - self._outPen.beginPath() - - def endPath(self): - self._outPen.endPath() - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - pt = self._transformPoint(pt) - self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs) - - def addComponent(self, glyphName, transformation): - transformation = self._transformation.transform(transformation) - self._outPen.addComponent(glyphName, transformation) - - -class GuessSmoothPointPen(AbstractPointPen): - - """Filtering PointPen that tries to determine whether an on-curve point - should be "smooth", ie. that it's a "tangent" point or a "curve" point. - """ - - def __init__(self, outPen): - self._outPen = outPen - self._points = None - - def _flushContour(self): - points = self._points - nPoints = len(points) - if not nPoints: - return - if points[0][1] == "move": - # Open path. - indices = range(1, nPoints - 1) - elif nPoints > 1: - # Closed path. To avoid having to mod the contour index, we - # simply abuse Python's negative index feature, and start at -1 - indices = range(-1, nPoints - 1) - else: - # closed path containing 1 point (!), ignore. - indices = [] - for i in indices: - pt, segmentType, dummy, name, kwargs = points[i] - if segmentType is None: - continue - prev = i - 1 - next = i + 1 - if points[prev][1] is not None and points[next][1] is not None: - continue - # At least one of our neighbors is an off-curve point - pt = points[i][0] - prevPt = points[prev][0] - nextPt = points[next][0] - if pt != prevPt and pt != nextPt: - dx1, dy1 = pt[0] - prevPt[0], pt[1] - prevPt[1] - dx2, dy2 = nextPt[0] - pt[0], nextPt[1] - pt[1] - a1 = math.atan2(dx1, dy1) - a2 = math.atan2(dx2, dy2) - if abs(a1 - a2) < 0.05: - points[i] = pt, segmentType, True, name, kwargs - - for pt, segmentType, smooth, name, kwargs in points: - self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs) - - def beginPath(self): - assert self._points is None - self._points = [] - self._outPen.beginPath() - - def endPath(self): - self._flushContour() - self._outPen.endPath() - self._points = None - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - self._points.append((pt, segmentType, False, name, kwargs)) - - def addComponent(self, glyphName, transformation): - assert self._points is None - self._outPen.addComponent(glyphName, transformation) - - -if __name__ == "__main__": - from fontTools.pens.basePen import _TestPen as PSPen - from robofab.pens.pointPen import PrintingPointPen - segmentPen = PSPen(None) -# pen = PointToSegmentPen(SegmentToPointPen(PointToSegmentPen(PSPen(None)))) - pen = PointToSegmentPen(SegmentToPointPen(PrintingPointPen())) -# pen = PrintingPointPen() - pen = PointToSegmentPen(PSPen(None), outputImpliedClosingLine=False) -# pen.beginPath() -# pen.addPoint((50, 50), name="an anchor") -# pen.endPath() - pen.beginPath() - pen.addPoint((-100, 0), segmentType="line") - pen.addPoint((0, 0), segmentType="line") - pen.addPoint((0, 100), segmentType="line") - pen.addPoint((30, 200)) - pen.addPoint((50, 100), name="superbezcontrolpoint!") - pen.addPoint((70, 200)) - pen.addPoint((100, 100), segmentType="curve") - pen.addPoint((100, 0), segmentType="line") - pen.endPath() -# pen.addComponent("a", (1, 0, 0, 1, 100, 200)) diff --git a/misc/pylib/robofab/pens/angledMarginPen.py b/misc/pylib/robofab/pens/angledMarginPen.py deleted file mode 100644 index 49ff8eed3..000000000 --- a/misc/pylib/robofab/pens/angledMarginPen.py +++ /dev/null @@ -1,132 +0,0 @@ -from robofab.world import RFont -from fontTools.pens.basePen import BasePen -from robofab.misc.arrayTools import updateBounds, pointInRect, unionRect -from robofab.misc.bezierTools import calcCubicBounds, calcQuadraticBounds -from robofab.pens.filterPen import _estimateCubicCurveLength, _getCubicPoint -import math - - - -__all__ = ["AngledMarginPen", "getAngledMargins", - "setAngledLeftMargin", "setAngledRightMargin", - "centerAngledMargins"] - - - -class AngledMarginPen(BasePen): - """ - Angled Margin Pen - - Pen to calculate the margins as if the margin lines were slanted - according to the font.info.italicAngle. - - Notes: - - this pen works on the on-curve points, and approximates the distance to curves. - - results will be float. - - when used in FontLab, the resulting margins may be slightly - different from the values originally set, due to rounding errors. - - similar to what RoboFog used to do. - - RoboFog had a special attribute for "italicoffset", horizontal - shift of all glyphs. This is missing in Robofab. - """ - def __init__(self, glyphSet, width, italicAngle): - BasePen.__init__(self, glyphSet) - self.width = width - self._angle = math.radians(90+italicAngle) - self.maxSteps = 100 - self.margin = None - self._left = None - self._right = None - self._start = None - self.currentPt = None - - def _getAngled(self, pt): - r = (g.width + (pt[1] / math.tan(self._angle)))-pt[0] - l = pt[0]-((pt[1] / math.tan(self._angle))) - if self._right is None: - self._right = r - else: - self._right = min(self._right, r) - if self._left is None: - self._left = l - else: - self._left = min(self._left, l) - #print pt, l, r - self.margin = self._left, self._right - - def _moveTo(self, pt): - self._start = self.currentPt = pt - - def _addMoveTo(self): - if self._start is None: - return - self._start = self.currentPt = None - - def _lineTo(self, pt): - self._addMoveTo() - self._getAngled(pt) - - def _curveToOne(self, pt1, pt2, pt3): - step = 1.0/self.maxSteps - factors = range(0, self.maxSteps+1) - for i in factors: - pt = _getCubicPoint(i*step, self.currentPt, pt1, pt2, pt3) - self._getAngled(pt) - self.currentPt = pt3 - - def _qCurveToOne(self, bcp, pt): - self._addMoveTo() - # add curve tracing magic here. - self._getAngled(pt) - self.currentPt = pt3 - -def getAngledMargins(glyph, font): - """Get the angled margins for this glyph.""" - pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) - glyph.draw(pen) - return pen.margin - -def setAngledLeftMargin(glyph, font, value): - """Set the left angled margin to value, adjusted for font.info.italicAngle.""" - pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) - g.draw(pen) - isLeft, isRight = pen.margin - glyph.leftMargin += value-isLeft - -def setAngledRightMargin(glyph, font, value): - """Set the right angled margin to value, adjusted for font.info.italicAngle.""" - pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) - g.draw(pen) - isLeft, isRight = pen.margin - glyph.rightMargin += value-isRight - -def centerAngledMargins(glyph, font): - """Center the glyph on angled margins.""" - pen = AngledMarginPen(font, glyph.width, font.info.italicAngle) - g.draw(pen) - isLeft, isRight = pen.margin - setAngledLeftMargin(glyph, font, (isLeft+isRight)*.5) - setAngledRightMargin(glyph, font, (isLeft+isRight)*.5) - -def guessItalicOffset(glyph, font): - """Guess the italic offset based on the margins of a symetric glyph. - For instance H or I. - """ - l, r = getAngledMargins(glyph, font) - return l - (l+r)*.5 - - -if __name__ == "__main__": - - # example for FontLab, with a glyph open. - from robofab.world import CurrentFont, CurrentGlyph - g = CurrentGlyph() - f = CurrentFont() - - print "margins!", getAngledMargins(g, f) - # set the angled margin to a value - m = 50 - setAngledLeftMargin(g, f, m) - setAngledRightMargin(g, f, m) - g.update() - diff --git a/misc/pylib/robofab/pens/boundsPen.pyx b/misc/pylib/robofab/pens/boundsPen.pyx deleted file mode 100644 index a1a72bd12..000000000 --- a/misc/pylib/robofab/pens/boundsPen.pyx +++ /dev/null @@ -1,95 +0,0 @@ -from fontTools.pens.basePen import BasePen -from robofab.misc.arrayTools import updateBounds, pointInRect, unionRect -from robofab.misc.bezierTools import calcCubicBounds, calcQuadraticBounds - - -__all__ = ["BoundsPen", "ControlBoundsPen"] - - -class ControlBoundsPen(BasePen): - - """Pen to calculate the "control bounds" of a shape. This is the - bounding box of all control points __on closed paths__, so may be larger than the - actual bounding box if there are curves that don't have points - on their extremes. - - Single points, or anchors, are ignored. - - When the shape has been drawn, the bounds are available as the - 'bounds' attribute of the pen object. It's a 4-tuple: - (xMin, yMin, xMax, yMax) - - This replaces fontTools/pens/boundsPen (temporarily?) - The fontTools bounds pen takes lose anchor points into account, - this one doesn't. - """ - - def __init__(self, glyphSet): - BasePen.__init__(self, glyphSet) - self.bounds = None - self._start = None - - def _moveTo(self, pt): - self._start = pt - - def _addMoveTo(self): - if self._start is None: - return - bounds = self.bounds - if bounds: - self.bounds = updateBounds(bounds, self._start) - else: - x, y = self._start - self.bounds = (x, y, x, y) - self._start = None - - def _lineTo(self, pt): - self._addMoveTo() - self.bounds = updateBounds(self.bounds, pt) - - def _curveToOne(self, bcp1, bcp2, pt): - self._addMoveTo() - bounds = self.bounds - bounds = updateBounds(bounds, bcp1) - bounds = updateBounds(bounds, bcp2) - bounds = updateBounds(bounds, pt) - self.bounds = bounds - - def _qCurveToOne(self, bcp, pt): - self._addMoveTo() - bounds = self.bounds - bounds = updateBounds(bounds, bcp) - bounds = updateBounds(bounds, pt) - self.bounds = bounds - - -class BoundsPen(ControlBoundsPen): - - """Pen to calculate the bounds of a shape. It calculates the - correct bounds even when the shape contains curves that don't - have points on their extremes. This is somewhat slower to compute - than the "control bounds". - - When the shape has been drawn, the bounds are available as the - 'bounds' attribute of the pen object. It's a 4-tuple: - (xMin, yMin, xMax, yMax) - """ - - def _curveToOne(self, bcp1, bcp2, pt): - self._addMoveTo() - bounds = self.bounds - bounds = updateBounds(bounds, pt) - if not pointInRect(bcp1, bounds) or not pointInRect(bcp2, bounds): - bounds = unionRect(bounds, calcCubicBounds( - self._getCurrentPoint(), bcp1, bcp2, pt)) - self.bounds = bounds - - def _qCurveToOne(self, bcp, pt): - self._addMoveTo() - bounds = self.bounds - bounds = updateBounds(bounds, pt) - if not pointInRect(bcp, bounds): - bounds = unionRect(bounds, calcQuadraticBounds( - self._getCurrentPoint(), bcp, pt)) - self.bounds = bounds - diff --git a/misc/pylib/robofab/pens/digestPen.py b/misc/pylib/robofab/pens/digestPen.py deleted file mode 100755 index 930daf468..000000000 --- a/misc/pylib/robofab/pens/digestPen.py +++ /dev/null @@ -1,106 +0,0 @@ -"""A couple of point pens which return the glyph as a list of basic values.""" - - -from robofab.pens.pointPen import AbstractPointPen - - -class DigestPointPen(AbstractPointPen): - - """Calculate a digest of all points - AND coordinates - AND components - in a glyph. - """ - - def __init__(self, ignoreSmoothAndName=False): - self._data = [] - self.ignoreSmoothAndName = ignoreSmoothAndName - - def beginPath(self): - self._data.append('beginPath') - - def endPath(self): - self._data.append('endPath') - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - if self.ignoreSmoothAndName: - self._data.append((pt, segmentType)) - else: - self._data.append((pt, segmentType, smooth, name)) - - def addComponent(self, baseGlyphName, transformation): - t = [] - for v in transformation: - if int(v) == v: - t.append(int(v)) - else: - t.append(v) - self._data.append((baseGlyphName, tuple(t))) - - def getDigest(self): - return tuple(self._data) - - def getDigestPointsOnly(self, needSort=True): - """ Return a tuple with all coordinates of all points, - but without smooth info or drawing instructions. - For instance if you want to compare 2 glyphs in shape, - but not interpolatability. - """ - points = [] - from types import TupleType - for item in self._data: - if type(item) == TupleType: - points.append(item[0]) - if needSort: - points.sort() - return tuple(points) - - -class DigestPointStructurePen(DigestPointPen): - - """Calculate a digest of the structure of the glyph - NOT coordinates - NOT values. - """ - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - self._data.append(segmentType) - - def addComponent(self, baseGlyphName, transformation): - self._data.append(baseGlyphName) - -if __name__ == "__main__": - """ - - beginPath - ((112, 651), 'line', False, None) - ((112, 55), 'line', False, None) - ((218, 55), 'line', False, None) - ((218, 651), 'line', False, None) - endPath - - """ - # a test - - from robofab.objects.objectsRF import RGlyph - - g = RGlyph() - p = g.getPen() - p.moveTo((112, 651)) - p.lineTo((112, 55)) - p.lineTo((218, 55)) - p.lineTo((218, 651)) - p.closePath() - - print g, len(g) - - digestPen = DigestPointPen() - g.drawPoints(digestPen) - - print - print "getDigest", digestPen.getDigest() - - print - print "getDigestPointsOnly", digestPen.getDigestPointsOnly() - -
\ No newline at end of file diff --git a/misc/pylib/robofab/pens/filterPen.py b/misc/pylib/robofab/pens/filterPen.py deleted file mode 100755 index f7c84c082..000000000 --- a/misc/pylib/robofab/pens/filterPen.py +++ /dev/null @@ -1,407 +0,0 @@ -"""A couple of point pens to filter contours in various ways.""" - -from fontTools.pens.basePen import AbstractPen, BasePen - -from robofab.pens.pointPen import AbstractPointPen -from robofab.objects.objectsRF import RGlyph as _RGlyph -from robofab.objects.objectsBase import _interpolatePt - -import math - -# -# threshold filtering -# - -def distance(pt1, pt2): - return math.hypot(pt1[0]-pt2[0], pt1[1]-pt2[1]) - -class ThresholdPointPen(AbstractPointPen): - - """ - Rewrite of the ThresholdPen as a PointPen - so that we can preserve named points and other arguments. - This pen will add components from the original glyph, but - but it won't filter those components. - - "move", "line", "curve" or "qcurve" - - """ - def __init__(self, otherPointPen, threshold=10): - self.threshold = threshold - self._lastPt = None - self._offCurveBuffer = [] - self.otherPointPen = otherPointPen - - def beginPath(self): - """Start a new sub path.""" - self.otherPointPen.beginPath() - self._lastPt = None - - def endPath(self): - """End the current sub path.""" - self.otherPointPen.endPath() - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - """Add a point to the current sub path.""" - if segmentType in ['curve', 'qcurve']: - # it's an offcurve, let's buffer them until we get another oncurve - # and we know what to do with them - self._offCurveBuffer.append((pt, segmentType, smooth, name, kwargs)) - return - - elif segmentType == "move": - # start of an open contour - self.otherPointPen.addPoint(pt, segmentType, smooth, name) # how to add kwargs? - self._lastPt = pt - self._offCurveBuffer = [] - - elif segmentType == "line": - if self._lastPt is None: - self.otherPointPen.addPoint(pt, segmentType, smooth, name) # how to add kwargs? - self._lastPt = pt - elif distance(pt, self._lastPt) >= self.threshold: - # we're oncurve and far enough from the last oncurve - if self._offCurveBuffer: - # empty any buffered offcurves - for buf_pt, buf_segmentType, buf_smooth, buf_name, buf_kwargs in self._offCurveBuffer: - self.otherPointPen.addPoint(buf_pt, buf_segmentType, buf_smooth, buf_name) # how to add kwargs? - self._offCurveBuffer = [] - # finally add the oncurve. - self.otherPointPen.addPoint(pt, segmentType, smooth, name) # how to add kwargs? - self._lastPt = pt - else: - # we're too short, so we're not going to make it. - # we need to clear out the offcurve buffer. - self._offCurveBuffer = [] - - def addComponent(self, baseGlyphName, transformation): - """Add a sub glyph. Note: this way components are not filtered.""" - self.otherPointPen.addComponent(baseGlyphName, transformation) - - -class ThresholdPen(AbstractPen): - - """Removes segments shorter in length than the threshold value.""" - - def __init__(self, otherPen, threshold=10): - self.threshold = threshold - self._lastPt = None - self.otherPen = otherPen - - def moveTo(self, pt): - self._lastPt = pt - self.otherPen.moveTo(pt) - - def lineTo(self, pt, smooth=False): - if self.threshold <= distance(pt, self._lastPt): - self.otherPen.lineTo(pt) - self._lastPt = pt - - def curveTo(self, pt1, pt2, pt3): - if self.threshold <= distance(pt3, self._lastPt): - self.otherPen.curveTo(pt1, pt2, pt3) - self._lastPt = pt3 - - def qCurveTo(self, *points): - if self.threshold <= distance(points[-1], self._lastPt): - self.otherPen.qCurveTo(*points) - self._lastPt = points[-1] - - def closePath(self): - self.otherPen.closePath() - - def endPath(self): - self.otherPen.endPath() - - def addComponent(self, glyphName, transformation): - self.otherPen.addComponent(glyphName, transformation) - - -def thresholdGlyph(aGlyph, threshold=10): - """ Convenience function that handles the filtering. """ - from robofab.pens.adapterPens import PointToSegmentPen - new = _RGlyph() - filterpen = ThresholdPen(new.getPen(), threshold) - wrappedPen = PointToSegmentPen(filterpen) - aGlyph.drawPoints(wrappedPen) - aGlyph.clear() - aGlyph.appendGlyph(new) - aGlyph.update() - return aGlyph - -def thresholdGlyphPointPen(aGlyph, threshold=10): - """ Same a thresholdGlyph, but using the ThresholdPointPen, which should respect anchors.""" - from robofab.pens.adapterPens import PointToSegmentPen - new = _RGlyph() - wrappedPen = new.getPointPen() - filterpen = ThresholdPointPen(wrappedPen, threshold) - aGlyph.drawPoints(filterpen) - aGlyph.clear() - new.drawPoints(aGlyph.getPointPen()) - aGlyph.update() - return aGlyph - - -# -# Curve flattening -# - -def _estimateCubicCurveLength(pt0, pt1, pt2, pt3, precision=10): - """Estimate the length of this curve by iterating - through it and averaging the length of the flat bits. - """ - points = [] - length = 0 - step = 1.0/precision - factors = range(0, precision+1) - for i in factors: - points.append(_getCubicPoint(i*step, pt0, pt1, pt2, pt3)) - for i in range(len(points)-1): - pta = points[i] - ptb = points[i+1] - length += distance(pta, ptb) - return length - -def _mid((x0, y0), (x1, y1)): - """(Point, Point) -> Point\nReturn the point that lies in between the two input points.""" - return 0.5 * (x0 + x1), 0.5 * (y0 + y1) - -def _getCubicPoint(t, pt0, pt1, pt2, pt3): - if t == 0: - return pt0 - if t == 1: - return pt3 - if t == 0.5: - a = _mid(pt0, pt1) - b = _mid(pt1, pt2) - c = _mid(pt2, pt3) - d = _mid(a, b) - e = _mid(b, c) - return _mid(d, e) - else: - cx = (pt1[0] - pt0[0]) * 3 - cy = (pt1[1] - pt0[1]) * 3 - bx = (pt2[0] - pt1[0]) * 3 - cx - by = (pt2[1] - pt1[1]) * 3 - cy - ax = pt3[0] - pt0[0] - cx - bx - ay = pt3[1] - pt0[1] - cy - by - t3 = t ** 3 - t2 = t * t - x = ax * t3 + bx * t2 + cx * t + pt0[0] - y = ay * t3 + by * t2 + cy * t + pt0[1] - return x, y - - -class FlattenPen(BasePen): - - """Process the contours into a series of straight lines by flattening the curves. - """ - - def __init__(self, otherPen, approximateSegmentLength=5, segmentLines=False, filterDoubles=True): - self.approximateSegmentLength = approximateSegmentLength - BasePen.__init__(self, {}) - self.otherPen = otherPen - self.currentPt = None - self.firstPt = None - self.segmentLines = segmentLines - self.filterDoubles = filterDoubles - - def _moveTo(self, pt): - self.otherPen.moveTo(pt) - self.currentPt = pt - self.firstPt = pt - - def _lineTo(self, pt): - if self.filterDoubles: - if pt == self.currentPt: - return - if not self.segmentLines: - self.otherPen.lineTo(pt) - self.currentPt = pt - return - d = distance(self.currentPt, pt) - maxSteps = int(round(d / self.approximateSegmentLength)) - if maxSteps < 1: - self.otherPen.lineTo(pt) - self.currentPt = pt - return - step = 1.0/maxSteps - factors = range(0, maxSteps+1) - for i in factors[1:]: - self.otherPen.lineTo(_interpolatePt(self.currentPt, pt, i*step)) - self.currentPt = pt - - def _curveToOne(self, pt1, pt2, pt3): - est = _estimateCubicCurveLength(self.currentPt, pt1, pt2, pt3)/self.approximateSegmentLength - maxSteps = int(round(est)) - falseCurve = (pt1==self.currentPt) and (pt2==pt3) - if maxSteps < 1 or falseCurve: - self.otherPen.lineTo(pt3) - self.currentPt = pt3 - return - step = 1.0/maxSteps - factors = range(0, maxSteps+1) - for i in factors[1:]: - pt = _getCubicPoint(i*step, self.currentPt, pt1, pt2, pt3) - self.otherPen.lineTo(pt) - self.currentPt = pt3 - - def _closePath(self): - self.lineTo(self.firstPt) - self.otherPen.closePath() - self.currentPt = None - - def _endPath(self): - self.otherPen.endPath() - self.currentPt = None - - def addComponent(self, glyphName, transformation): - self.otherPen.addComponent(glyphName, transformation) - - -def flattenGlyph(aGlyph, threshold=10, segmentLines=True): - - """Replace curves with series of straight l ines.""" - - from robofab.pens.adapterPens import PointToSegmentPen - if len(aGlyph.contours) == 0: - return - new = _RGlyph() - writerPen = new.getPen() - filterpen = FlattenPen(writerPen, threshold, segmentLines) - wrappedPen = PointToSegmentPen(filterpen) - aGlyph.drawPoints(wrappedPen) - aGlyph.clear() - aGlyph.appendGlyph(new) - aGlyph.update() - return aGlyph - - -def spikeGlyph(aGlyph, segmentLength=20, spikeLength=40, patternFunc=None): - - """Add narly spikes or dents to the glyph. - patternFunc is an optional function which recalculates the offset.""" - - from math import atan2, sin, cos, pi - - new = _RGlyph() - new.appendGlyph(aGlyph) - new.width = aGlyph.width - - if len(new.contours) == 0: - return - flattenGlyph(new, segmentLength, segmentLines=True) - for contour in new: - l = len(contour.points) - lastAngle = None - for i in range(0, len(contour.points), 2): - prev = contour.points[i-1] - cur = contour.points[i] - next = contour.points[(i+1)%l] - angle = atan2(prev.x - next.x, prev.y - next.y) - lastAngle = angle - if patternFunc is not None: - thisSpikeLength = patternFunc(spikeLength) - else: - thisSpikeLength = spikeLength - cur.x -= sin(angle+.5*pi)*thisSpikeLength - cur.y -= cos(angle+.5*pi)*thisSpikeLength - new.update() - aGlyph.clear() - aGlyph.appendGlyph(new) - aGlyph.update() - return aGlyph - - -def halftoneGlyph(aGlyph, invert=False): - - """Convert the glyph into some sort of halftoning pattern. - Measure a bunch of inside/outside points to simulate grayscale levels. - Slow. - """ - print 'halftoneGlyph is running...' - grid = {} - drawing = {} - dataDistance = 10 - scan = 2 - preload = 0 - cellDistance = dataDistance * 5 - overshoot = dataDistance * 2 - (xMin, yMin, xMax, yMax) = aGlyph.box - for x in range(xMin-overshoot, xMax+overshoot, dataDistance): - print 'scanning..', x - for y in range(yMin-overshoot, yMax+overshoot, dataDistance): - if aGlyph.pointInside((x, y)): - grid[(x, y)] = True - else: - grid[(x, y)] = False - #print 'gathering data', x, y, grid[(x, y)] - print 'analyzing..' - for x in range(xMin-overshoot, xMax+overshoot, cellDistance): - for y in range(yMin-overshoot, yMax+overshoot, cellDistance): - total = preload - for scanx in range(-scan, scan): - for scany in range(-scan, scan): - if grid.get((x+scanx*dataDistance, y+scany*dataDistance)): - total += 1 - if invert: - drawing[(x, y)] = 2*scan**2 - float(total) - else: - drawing[(x, y)] = float(total) - aGlyph.clear() - print drawing - for (x,y) in drawing.keys(): - size = drawing[(x,y)] / float(2*scan**2) * 5 - pen = aGlyph.getPen() - pen.moveTo((x-size, y-size)) - pen.lineTo((x+size, y-size)) - pen.lineTo((x+size, y+size)) - pen.lineTo((x-size, y+size)) - pen.lineTo((x-size, y-size)) - pen.closePath() - aGlyph.update() - - -if __name__ == "__main__": - from robofab.pens.pointPen import PrintingPointPen - pp = PrintingPointPen() - #pp.beginPath() - #pp.addPoint((100, 100)) - #pp.endPath() - - tpp = ThresholdPointPen(pp, threshold=20) - tpp.beginPath() - #segmentType=None, smooth=False, name=None - tpp.addPoint((100, 100), segmentType="line", smooth=True) - # section that should be too small - tpp.addPoint((100, 102), segmentType="line", smooth=True) - tpp.addPoint((200, 200), segmentType="line", smooth=True) - # curve section with final point that's far enough, but with offcurves that are under the threshold - tpp.addPoint((200, 205), segmentType="curve", smooth=True) - tpp.addPoint((300, 295), segmentType="curve", smooth=True) - tpp.addPoint((300, 300), segmentType="line", smooth=True) - # curve section with final point that is not far enough - tpp.addPoint((550, 350), segmentType="curve", smooth=True) - tpp.addPoint((360, 760), segmentType="curve", smooth=True) - tpp.addPoint((310, 310), segmentType="line", smooth=True) - - tpp.addPoint((400, 400), segmentType="line", smooth=True) - tpp.addPoint((100, 100), segmentType="line", smooth=True) - tpp.endPath() - - # couple of single points with names - tpp.beginPath() - tpp.addPoint((500, 500), segmentType="move", smooth=True, name="named point") - tpp.addPoint((600, 500), segmentType="move", smooth=True, name="named point") - tpp.addPoint((601, 501), segmentType="move", smooth=True, name="named point") - tpp.endPath() - - # open path - tpp.beginPath() - tpp.addPoint((500, 500), segmentType="move", smooth=True) - tpp.addPoint((501, 500), segmentType="line", smooth=True) - tpp.addPoint((101, 500), segmentType="line", smooth=True) - tpp.addPoint((101, 100), segmentType="line", smooth=True) - tpp.addPoint((498, 498), segmentType="line", smooth=True) - tpp.endPath() -
\ No newline at end of file diff --git a/misc/pylib/robofab/pens/flPen.py b/misc/pylib/robofab/pens/flPen.py deleted file mode 100755 index d5a867cd9..000000000 --- a/misc/pylib/robofab/pens/flPen.py +++ /dev/null @@ -1,274 +0,0 @@ -"""Pens for creating glyphs in FontLab.""" - - -__all__ = ["FLPen", "FLPointPen", "drawFLGlyphOntoPointPen"] - - -from FL import * - -try: - from fl_cmd import * -except ImportError: - print "The fl_cmd module is not available here. flPen.py" - -from robofab.tools.toolsFL import NewGlyph -from robofab.pens.pointPen import AbstractPointPen -from robofab.pens.adapterPens import SegmentToPointPen - - -def roundInt(x): - return int(round(x)) - - -class FLPen(SegmentToPointPen): - - def __init__(self, glyph): - SegmentToPointPen.__init__(self, FLPointPen(glyph)) - - -class FLPointPen(AbstractPointPen): - - def __init__(self, glyph): - if hasattr(glyph, "isRobofab"): - self.glyph = glyph.naked() - else: - self.glyph = glyph - self.currentPath = None - - def beginPath(self): - self.currentPath = [] - - def endPath(self): - # Love is... abstracting away FL's madness. - path = self.currentPath - self.currentPath = None - glyph = self.glyph - if len(path) == 1 and path[0][3] is not None: - # Single point on the contour, it has a name. Make it an anchor. - x, y = path[0][0] - name = path[0][3] - anchor = Anchor(name, roundInt(x), roundInt(y)) - glyph.anchors.append(anchor) - return - firstOnCurveIndex = None - for i in range(len(path)): - if path[i][1] is not None: - firstOnCurveIndex = i - break - if firstOnCurveIndex is None: - # TT special case: on-curve-less contour. FL doesn't support that, - # so we insert an implied point at the end. - x1, y1 = path[0][0] - x2, y2 = path[-1][0] - impliedPoint = 0.5 * (x1 + x2), 0.5 * (y1 + y2) - path.append((impliedPoint, "qcurve", True, None)) - firstOnCurveIndex = 0 - path = path[firstOnCurveIndex + 1:] + path[:firstOnCurveIndex + 1] - firstPoint, segmentType, smooth, name = path[-1] - closed = True - if segmentType == "move": - path = path[:-1] - closed = False - # XXX The contour is not closed, but I can't figure out how to - # create an open contour in FL. Creating one by hand shows type"0x8011" - # for a move node in an open contour, but I'm not able to access - # that flag. - elif segmentType == "line": - # The contour is closed and ends in a lineto, which is redundant - # as it's implied by closepath. - path = path[:-1] - x, y = firstPoint - node = Node(nMOVE, Point(roundInt(x), roundInt(y))) - if smooth and closed: - if segmentType == "line" or path[0][1] == "line": - node.alignment = nFIXED - else: - node.alignment = nSMOOTH - glyph.Insert(node, len(glyph)) - segment = [] - nPoints = len(path) - for i in range(nPoints): - pt, segmentType, smooth, name = path[i] - segment.append(pt) - if segmentType is None: - continue - if segmentType == "curve": - if len(segment) < 2: - segmentType = "line" - elif len(segment) == 2: - segmentType = "qcurve" - if segmentType == "qcurve": - for x, y in segment[:-1]: - glyph.Insert(Node(nOFF, Point(roundInt(x), roundInt(y))), len(glyph)) - x, y = segment[-1] - node = Node(nLINE, Point(roundInt(x), roundInt(y))) - glyph.Insert(node, len(glyph)) - elif segmentType == "curve": - if len(segment) == 3: - cubicSegments = [segment] - else: - from fontTools.pens.basePen import decomposeSuperBezierSegment - cubicSegments = decomposeSuperBezierSegment(segment) - nSegments = len(cubicSegments) - for i in range(nSegments): - pt1, pt2, pt3 = cubicSegments[i] - x, y = pt3 - node = Node(nCURVE, Point(roundInt(x), roundInt(y))) - node.points[1].x, node.points[1].y = roundInt(pt1[0]), roundInt(pt1[1]) - node.points[2].x, node.points[2].y = roundInt(pt2[0]), roundInt(pt2[1]) - if i != nSegments - 1: - node.alignment = nSMOOTH - glyph.Insert(node, len(self.glyph)) - elif segmentType == "line": - assert len(segment) == 1, segment - x, y = segment[0] - node = Node(nLINE, Point(roundInt(x), roundInt(y))) - glyph.Insert(node, len(glyph)) - else: - assert 0, "unsupported curve type (%s)" % segmentType - if smooth: - if i + 1 < nPoints or closed: - # Can't use existing node, as you can't change node attributes - # AFTER it's been appended to the glyph. - node = glyph[-1] - if segmentType == "line" or path[(i+1) % nPoints][1] == "line": - # tangent - node.alignment = nFIXED - else: - # curve - node.alignment = nSMOOTH - segment = [] - if closed: - # we may have output a node too much - node = glyph[-1] - if node.type == nLINE and (node.x, node.y) == (roundInt(firstPoint[0]), roundInt(firstPoint[1])): - glyph.DeleteNode(len(glyph) - 1) - - def addPoint(self, pt, segmentType=None, smooth=None, name=None, **kwargs): - self.currentPath.append((pt, segmentType, smooth, name)) - - def addComponent(self, baseName, transformation): - assert self.currentPath is None - # make base glyph if needed, Component() needs the index - NewGlyph(self.glyph.parent, baseName, updateFont=False) - baseIndex = self.glyph.parent.FindGlyph(baseName) - if baseIndex == -1: - raise KeyError, "couldn't find or make base glyph" - xx, xy, yx, yy, dx, dy = transformation - # XXX warn when xy or yx != 0 - new = Component(baseIndex, Point(dx, dy), Point(xx, yy)) - self.glyph.components.append(new) - - -def drawFLGlyphOntoPointPen(flGlyph, pen): - """Draw a FontLab glyph onto a PointPen.""" - for anchor in flGlyph.anchors: - pen.beginPath() - pen.addPoint((anchor.x, anchor.y), name=anchor.name) - pen.endPath() - for contour in _getContours(flGlyph): - pen.beginPath() - for pt, segmentType, smooth in contour: - pen.addPoint(pt, segmentType=segmentType, smooth=smooth) - pen.endPath() - for baseGlyph, tranform in _getComponents(flGlyph): - pen.addComponent(baseGlyph, tranform) - - - -class FLPointContourPen(FLPointPen): - """Same as FLPointPen, except that it ignores components.""" - def addComponent(self, baseName, transformation): - pass - - -NODE_TYPES = {nMOVE: "move", nLINE: "line", nCURVE: "curve", - nOFF: None} - -def _getContours(glyph): - contours = [] - for i in range(len(glyph)): - node = glyph[i] - segmentType = NODE_TYPES[node.type] - if segmentType == "move": - contours.append([]) - for pt in node.points[1:]: - contours[-1].append(((pt.x, pt.y), None, False)) - smooth = node.alignment != nSHARP - contours[-1].append(((node.x, node.y), segmentType, smooth)) - - for contour in contours: - # filter out or change the move - movePt, segmentType, smooth = contour[0] - assert segmentType == "move" - lastSegmentType = contour[-1][1] - if movePt == contour[-1][0] and lastSegmentType == "curve": - contour[0] = contour[-1] - contour.pop() - elif lastSegmentType is None: - contour[0] = movePt, "qcurve", smooth - else: - assert lastSegmentType in ("line", "curve") - contour[0] = movePt, "line", smooth - - # change "line" to "qcurve" if appropriate - previousSegmentType = "ArbitraryValueOtherThanNone" - for i in range(len(contour)): - pt, segmentType, smooth = contour[i] - if segmentType == "line" and previousSegmentType is None: - contour[i] = pt, "qcurve", smooth - previousSegmentType = segmentType - - return contours - - -def _simplifyValues(*values): - """Given a set of numbers, convert items to ints if they are - integer float values, eg. 0.0, 1.0.""" - newValues = [] - for v in values: - i = int(v) - if v == i: - v = i - newValues.append(v) - return newValues - - -def _getComponents(glyph): - components = [] - for comp in glyph.components: - baseName = glyph.parent[comp.index].name - dx, dy = comp.delta.x, comp.delta.y - sx, sy = comp.scale.x, comp.scale.y - dx, dy, sx, sy = _simplifyValues(dx, dy, sx, sy) - components.append((baseName, (sx, 0, 0, sy, dx, dy))) - return components - - -def test(): - g = fl.glyph - g.Clear() - - p = PLPen(g) - p.moveTo((50, 50)) - p.lineTo((150,50)) - p.lineTo((170, 200), smooth=2) - p.curveTo((173, 225), (150, 250), (120, 250), smooth=1) - p.curveTo((85, 250), (50, 200), (50, 200)) - p.closePath() - - p.moveTo((300, 300)) - p.lineTo((400, 300)) - p.curveTo((450, 325), (450, 375), (400, 400)) - p.qCurveTo((400, 500), (350, 550), (300, 500), (300, 400)) - p.closePath() - p.setWidth(600) - p.setNote("Hello, this is a note") - p.addAnchor("top", (250, 600)) - - fl.UpdateGlyph(-1) - fl.UpdateFont(-1) - - -if __name__ == "__main__": - test() diff --git a/misc/pylib/robofab/pens/marginPen.py b/misc/pylib/robofab/pens/marginPen.py deleted file mode 100644 index 03f13f917..000000000 --- a/misc/pylib/robofab/pens/marginPen.py +++ /dev/null @@ -1,155 +0,0 @@ -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() - diff --git a/misc/pylib/robofab/pens/mathPens.py b/misc/pylib/robofab/pens/mathPens.py deleted file mode 100755 index 1fe1026e1..000000000 --- a/misc/pylib/robofab/pens/mathPens.py +++ /dev/null @@ -1,185 +0,0 @@ -"""Some pens that are needed during glyph math""" - - -from robofab.pens.pointPen import BasePointToSegmentPen, AbstractPointPen - - -class GetMathDataPointPen(AbstractPointPen): - - """ - Point pen that converts all "line" segments into - curve segments containing two off curve points. - """ - - def __init__(self): - self.contours = [] - self.components = [] - self.anchors = [] - self._points = [] - - def _flushContour(self): - points = self._points - if len(points) == 1: - segmentType, pt, smooth, name = points[0] - self.anchors.append((pt, name)) - else: - self.contours.append([]) - prevOnCurve = None - offCurves = [] - # deal with the first point - segmentType, pt, smooth, name = points[0] - # if it is an offcurve, add it to the offcurve list - if segmentType is None: - offCurves.append((segmentType, pt, smooth, name)) - # if it is a line, change the type to curve and add it to the contour - # create offcurves corresponding with the last oncurve and - # this point and add them to the points list - elif segmentType == "line": - prevOnCurve = pt - self.contours[-1].append(("curve", pt, False, name)) - lastPoint = points[-1][1] - points.append((None, lastPoint, False, None)) - points.append((None, pt, False, None)) - # a move, curve or qcurve. simple append the data. - else: - self.contours[-1].append((segmentType, pt, smooth, name)) - prevOnCurve = pt - # now go through the rest of the points - for segmentType, pt, smooth, name in points[1:]: - # store the off curves - if segmentType is None: - offCurves.append((segmentType, pt, smooth, name)) - continue - # make off curve corresponding the the previous - # on curve an dthis point - if segmentType == "line": - segmentType = "curve" - offCurves.append((None, prevOnCurve, False, None)) - offCurves.append((None, pt, False, None)) - # add the offcurves to the contour - for offCurve in offCurves: - self.contours[-1].append(offCurve) - # add the oncurve to the contour - self.contours[-1].append((segmentType, pt, smooth, name)) - # reset the stored data - prevOnCurve = pt - offCurves = [] - # catch offcurves that belong to the first - if len(offCurves) != 0: - self.contours[-1].extend(offCurves) - - def beginPath(self): - self._points = [] - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - self._points.append((segmentType, pt, smooth, name)) - - def endPath(self): - self._flushContour() - - def addComponent(self, baseGlyphName, transformation): - self.components.append((baseGlyphName, transformation)) - - def getData(self): - return { - 'contours':list(self.contours), - 'components':list(self.components), - 'anchors':list(self.anchors) - } - - -class CurveSegmentFilterPointPen(AbstractPointPen): - - """ - Point pen that turns curve segments that contain - unnecessary anchor points into line segments. - """ - # XXX it would be great if this also checked to see if the - # off curves are on the segment and therefre unneeded - - def __init__(self, anotherPointPen): - self._pen = anotherPointPen - self._points = [] - - def _flushContour(self): - points = self._points - # an anchor - if len(points) == 1: - pt, segmentType, smooth, name = points[0] - self._pen.addPoint(pt, segmentType, smooth, name) - else: - prevOnCurve = None - offCurves = [] - - pointsToDraw = [] - - # deal with the first point - pt, segmentType, smooth, name = points[0] - # if it is an offcurve, add it to the offcurve list - if segmentType is None: - offCurves.append((pt, segmentType, smooth, name)) - else: - # potential redundancy - if segmentType == "curve": - # gather preceding off curves - testOffCurves = [] - lastPoint = None - for i in xrange(len(points)): - i = -i - 1 - testPoint = points[i] - testSegmentType = testPoint[1] - if testSegmentType is not None: - lastPoint = testPoint[0] - break - testOffCurves.append(testPoint[0]) - # if two offcurves exist we can test for redundancy - if len(testOffCurves) == 2: - if testOffCurves[1] == lastPoint and testOffCurves[0] == pt: - segmentType = "line" - # remove the last two points - points = points[:-2] - # add the point to the contour - pointsToDraw.append((pt, segmentType, smooth, name)) - prevOnCurve = pt - for pt, segmentType, smooth, name in points[1:]: - # store offcurves - if segmentType is None: - offCurves.append((pt, segmentType, smooth, name)) - continue - # curves are a potential redundancy - elif segmentType == "curve": - if len(offCurves) == 2: - # test for redundancy - if offCurves[0][0] == prevOnCurve and offCurves[1][0] == pt: - offCurves = [] - segmentType = "line" - # add all offcurves - for offCurve in offCurves: - pointsToDraw.append(offCurve) - # add the on curve - pointsToDraw.append((pt, segmentType, smooth, name)) - # reset the stored data - prevOnCurve = pt - offCurves = [] - # catch any remaining offcurves - if len(offCurves) != 0: - for offCurve in offCurves: - pointsToDraw.append(offCurve) - # draw to the pen - for pt, segmentType, smooth, name in pointsToDraw: - self._pen.addPoint(pt, segmentType, smooth, name) - - def beginPath(self): - self._points = [] - self._pen.beginPath() - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - self._points.append((pt, segmentType, smooth, name)) - - def endPath(self): - self._flushContour() - self._pen.endPath() - - def addComponent(self, baseGlyphName, transformation): - self._pen.addComponent(baseGlyphName, transformation) - diff --git a/misc/pylib/robofab/pens/pointPen.py b/misc/pylib/robofab/pens/pointPen.py deleted file mode 100644 index dc81df38e..000000000 --- a/misc/pylib/robofab/pens/pointPen.py +++ /dev/null @@ -1,173 +0,0 @@ -__all__ = ["AbstractPointPen", "BasePointToSegmentPen", "PrintingPointPen", - "PrintingSegmentPen", "SegmentPrintingPointPen"] - - -class AbstractPointPen: - - def beginPath(self): - """Start a new sub path.""" - raise NotImplementedError - - def endPath(self): - """End the current sub path.""" - raise NotImplementedError - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - """Add a point to the current sub path.""" - raise NotImplementedError - - def addComponent(self, baseGlyphName, transformation): - """Add a sub glyph.""" - raise NotImplementedError - - -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): - 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): - self.havePath = True - print "pen.beginPath()" - def endPath(self): - self.havePath = False - print "pen.endPath()" - def addPoint(self, pt, segmentType=None, smooth=False, name=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 kwargs: - args.append("**%s" % kwargs) - print "pen.addPoint(%s)" % ", ".join(args) - def addComponent(self, baseGlyphName, transformation): - assert not self.havePath - print "pen.addComponent(%r, %s)" % (baseGlyphName, tuple(transformation)) - - -from fontTools.pens.basePen import AbstractPen - -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) diff --git a/misc/pylib/robofab/pens/quartzPen.py b/misc/pylib/robofab/pens/quartzPen.py deleted file mode 100644 index dd1947ccf..000000000 --- a/misc/pylib/robofab/pens/quartzPen.py +++ /dev/null @@ -1,21 +0,0 @@ -from fontTools.pens.basePen import BasePen - -class QuartzPen(BasePen): - - """Pen to draw onto a Quartz drawing context (Carbon.CG).""" - - def __init__(self, glyphSet, quartzContext): - BasePen.__init__(self, glyphSet) - self._context = quartzContext - - def _moveTo(self, (x, y)): - self._context.CGContextMoveToPoint(x, y) - - def _lineTo(self, (x, y)): - self._context.CGContextAddLineToPoint(x, y) - - def _curveToOne(self, (x1, y1), (x2, y2), (x3, y3)): - self._context.CGContextAddCurveToPoint(x1, y1, x2, y2, x3, y3) - - def _closePath(self): - self._context.closePath() diff --git a/misc/pylib/robofab/pens/reverseContourPointPen.py b/misc/pylib/robofab/pens/reverseContourPointPen.py deleted file mode 100755 index 8ce001b4d..000000000 --- a/misc/pylib/robofab/pens/reverseContourPointPen.py +++ /dev/null @@ -1,125 +0,0 @@ -"""PointPen for reversing the winding direction of contours.""" - - -__all__ = ["ReverseContourPointPen"] - - -from robofab.pens.pointPen import AbstractPointPen - - -class ReverseContourPointPen(AbstractPointPen): - - """This is a PointPen that passes outline data to another PointPen, but - reversing the winding direction of all contours. Components are simply - passed through unchanged. - - Closed contours are reversed in such a way that the first point remains - the first point. - """ - - def __init__(self, outputPointPen): - self.pen = outputPointPen - self.currentContour = None # a place to store the points for the current sub path - - def _flushContour(self): - pen = self.pen - contour = self.currentContour - if not contour: - pen.beginPath() - pen.endPath() - return - - closed = contour[0][1] != "move" - if not closed: - lastSegmentType = "move" - else: - # Remove the first point and insert it at the end. When - # the list of points gets reversed, this point will then - # again be at the start. In other words, the following - # will hold: - # for N in range(len(originalContour)): - # originalContour[N] == reversedContour[-N] - contour.append(contour.pop(0)) - # Find the first on-curve point. - firstOnCurve = None - for i in range(len(contour)): - if contour[i][1] is not None: - firstOnCurve = i - break - if firstOnCurve is None: - # There are no on-curve points, be basically have to - # do nothing but contour.reverse(). - lastSegmentType = None - else: - lastSegmentType = contour[firstOnCurve][1] - - contour.reverse() - if not closed: - # Open paths must start with a move, so we simply dump - # all off-curve points leading up to the first on-curve. - while contour[0][1] is None: - contour.pop(0) - pen.beginPath() - for pt, nextSegmentType, smooth, name in contour: - if nextSegmentType is not None: - segmentType = lastSegmentType - lastSegmentType = nextSegmentType - else: - segmentType = None - pen.addPoint(pt, segmentType=segmentType, smooth=smooth, name=name) - pen.endPath() - - def beginPath(self): - assert self.currentContour is None - self.currentContour = [] - self.onCurve = [] - - def endPath(self): - assert self.currentContour is not None - self._flushContour() - self.currentContour = None - - def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs): - self.currentContour.append((pt, segmentType, smooth, name)) - - def addComponent(self, glyphName, transform): - assert self.currentContour is None - self.pen.addComponent(glyphName, transform) - - -if __name__ == "__main__": - from robofab.pens.pointPen import PrintingPointPen - pP = PrintingPointPen() - rP = ReverseContourPointPen(pP) - - rP.beginPath() - rP.addPoint((339, -8), "curve") - rP.addPoint((502, -8)) - rP.addPoint((635, 65)) - rP.addPoint((635, 305), "curve") - rP.addPoint((635, 545)) - rP.addPoint((504, 623)) - rP.addPoint((340, 623), "curve") - rP.addPoint((177, 623)) - rP.addPoint((43, 545)) - rP.addPoint((43, 305), "curve") - rP.addPoint((43, 65)) - rP.addPoint((176, -8)) - rP.endPath() - - rP.beginPath() - rP.addPoint((100, 100), "move", smooth=False, name='a') - rP.addPoint((150, 150)) - rP.addPoint((200, 200)) - rP.addPoint((250, 250), "curve", smooth=True, name='b') - rP.addPoint((300, 300), "line", smooth=False, name='c') - rP.addPoint((350, 350)) - rP.addPoint((400, 400)) - rP.addPoint((450, 450)) - rP.addPoint((500, 500), "curve", smooth=True, name='d') - rP.addPoint((550, 550)) - rP.addPoint((600, 600)) - rP.addPoint((650, 650)) - rP.addPoint((700, 700)) - rP.addPoint((750, 750), "qcurve", smooth=False, name='e') - rP.endPath() diff --git a/misc/pylib/robofab/pens/rfUFOPen.pyx b/misc/pylib/robofab/pens/rfUFOPen.pyx deleted file mode 100755 index 265d7aea0..000000000 --- a/misc/pylib/robofab/pens/rfUFOPen.pyx +++ /dev/null @@ -1,103 +0,0 @@ -"""Pens for creating UFO glyphs.""" - -from robofab.objects.objectsBase import MOVE, LINE, CORNER, CURVE, QCURVE, OFFCURVE -from robofab.objects.objectsRF import RContour, RSegment, RPoint -from robofab.pens.pointPen import BasePointToSegmentPen -from robofab.pens.adapterPens import SegmentToPointPen - - -class RFUFOPen(SegmentToPointPen): - - def __init__(self, glyph): - SegmentToPointPen.__init__(self, RFUFOPointPen(glyph)) - - -class RFUFOPointPen(BasePointToSegmentPen): - - """Point pen for building objectsRF glyphs""" - - def __init__(self, glyph): - BasePointToSegmentPen.__init__(self) - self.glyph = glyph - - def _flushContour(self, segments): - # - # adapted from robofab.pens.adapterPens.PointToSegmentPen - # - assert len(segments) >= 1 - # if we only have one point and it has a name, we must have an anchor - first = segments[0] - segmentType, points = first - pt, smooth, name, kwargs = points[0] - if len(segments) == 1 and name != None: - self.glyph.appendAnchor(name, pt) - return - # we must have a contour - contour = RContour() - contour.setParent(self.glyph) - if segments[0][0] == "move": - # It's an open path. - closed = False - points = segments[0][1] - assert len(points) == 1 - movePt, smooth, name, kwargs = points[0] - del segments[0] - else: - # It's a closed path, do a moveTo to the last - # point of the last segment. only if it isn't a qcurve - closed = True - segmentType, points = segments[-1] - movePt, smooth, name, kwargs = points[-1] - ## THIS IS STILL UNDECIDED!!! - # since objectsRF currently follows the FL model of not - # allowing open contours, remove the last segment - # since it is being replaced by a move - if segmentType == 'line': - del segments[-1] - # construct a move segment and apply it to the contour if we aren't dealing with a qcurve - segment = RSegment() - segment.setParent(contour) - segment.smooth = smooth - rPoint = RPoint(x=movePt[0], y=movePt[1], pointType=MOVE, name=name) - rPoint.setParent(segment) - segment.points = [rPoint] - contour.segments.append(segment) - # do the rest of the segments - for segmentType, points in segments: - points = [(pt, name) for pt, smooth, name, kwargs in points] - if segmentType == "line": - assert len(points) == 1 - sType = LINE - elif segmentType == "curve": - sType = CURVE - elif segmentType == "qcurve": - sType = QCURVE - else: - assert 0, "illegal segmentType: %s" % segmentType - segment = RSegment() - segment.setParent(contour) - segment.smooth = smooth - rPoints = [] - # handle the offCurves - for point in points[:-1]: - point, name = point - rPoint = RPoint(x=point[0], y=point[1], pointType=OFFCURVE, name=name) - rPoint.setParent(segment) - rPoints.append(rPoint) - # now the onCurve - point, name = points[-1] - rPoint = RPoint(x=point[0], y=point[1], pointType=sType, name=name) - rPoint.setParent(segment) - rPoints.append(rPoint) - # apply them to the segment - segment.points = rPoints - contour.segments.append(segment) - if contour.segments[-1].type == "curve": - contour.segments[-1].points[-1].name = None - self.glyph.contours.append(contour) - - def addComponent(self, glyphName, transform): - xx, xy, yx, yy, dx, dy = transform - self.glyph.appendComponent(baseGlyph=glyphName, offset=(dx, dy), scale=(xx, yy)) - - |