summaryrefslogtreecommitdiff
path: root/misc/pylib/robofab/pens
diff options
context:
space:
mode:
Diffstat (limited to 'misc/pylib/robofab/pens')
-rwxr-xr-xmisc/pylib/robofab/pens/__init__.py11
-rw-r--r--misc/pylib/robofab/pens/adapterPens.py293
-rw-r--r--misc/pylib/robofab/pens/angledMarginPen.py132
-rw-r--r--misc/pylib/robofab/pens/boundsPen.pyx95
-rwxr-xr-xmisc/pylib/robofab/pens/digestPen.py106
-rwxr-xr-xmisc/pylib/robofab/pens/filterPen.py407
-rwxr-xr-xmisc/pylib/robofab/pens/flPen.py274
-rw-r--r--misc/pylib/robofab/pens/marginPen.py155
-rwxr-xr-xmisc/pylib/robofab/pens/mathPens.py185
-rw-r--r--misc/pylib/robofab/pens/pointPen.py173
-rw-r--r--misc/pylib/robofab/pens/quartzPen.py21
-rwxr-xr-xmisc/pylib/robofab/pens/reverseContourPointPen.py125
-rwxr-xr-xmisc/pylib/robofab/pens/rfUFOPen.pyx103
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))
-
-