Using pens ↩
Adapted from the original RoboFab documentation.
- Drawing stuff in glyphs
On the pens page we discussed the different kinds of pen and their methods. Here we’ll look at some practical applications of pens.
Drawing stuff in glyphs
Sometimes it is necessary to draw things in glyph with a script. Boxes around glyphs, logos, patterns. Whatever the reason, a pen is the way to do it. Rather than constructing all the Contour and Segment objects yourself (it’s complex and it can be a pain to get it right), just use a pen to tell the Glyph what you want to draw.
In order to make it somewhat easier to find the right pen for the right glyph,
RGlyph.getPen() will return a pen object for the current environment. So, in UFO-based fonts,
getPen() returns a pen for drawing in UFO glyphs. If you’re in FontLab using RoboFabA Python library with objects that deal with data usually associated with fonts and type design. Supports the UFO font format.
Officially retired in March 2018. Replaced by FontParts.,
getPen() returns a pen fit for drawing in FontLab glyphs. This way you can keep the code free of environment-specific imports.
f = CurrentFont() newGlyph = f.newGlyph('demoDrawGlyph') newGlyph.clear() newGlyph.width = 1000 # hey, what's this: pen = newGlyph.getPen() # ha! a sneaky way to get a pen object! pen.moveTo((100, 100)) pen.lineTo((800, 100)) pen.curveTo((1000, 300), (1000, 600), (800, 800)) pen.lineTo((100, 800)) pen.lineTo((100, 100)) pen.closePath() newGlyph.changed()
Using pens to get to contour and outline data.
DigestPointPen is a
pointsPen and it doesn’t draw anything, but collects all the coordinates and drawing instructions from
glyph.drawPoints(). When the drawing is done you can get to the data with
myPen.getDigest(). The result is a tuple with a series of coordinates and instructions. Because it is a tuple you can use it to compare it to other digests, for instance if you want to test if two glyphs are the same or not.
from fontPens.digestPointPen import DigestPointPen f = CurrentFont() myPen = DigestPointPen() f['period'].drawPoints(myPen) print(myPen.getDigest())
('beginPath', ((140, -4), 'curve', True, None), ((103, -4), None, None, None), ((71, 30), None, None, None), ((71, 69), 'curve', True, None), ((71, 109), None, None, None), ((103, 143), None, None, None), ((140, 143), 'curve', True, None), ((178, 143), None, None, None), ((210, 109), None, None, None), ((210, 69), 'curve', True, None), ((210, 30), None, None, None), ((178, -4), None, None, None), 'endPath')
DigestPointStructurePen is very similar to the
DigestPointPen, but it only returns a tuple of the structure without any coordinates. This is very useful in comparing point structures between glyphs. For instance if you want to test if the glyphs can be used in interpolation. Using the same glyph as the example above, compare the results.
from fontPens.digestPointPen import DigestPointStructurePen f = OpenFont() myPen = DigestPointStructurePen() f['period'].drawPoints(myPen) print(myPen.getDigest())
('beginPath', 'curve', None, None, 'curve', None, None, 'curve', None, None, 'curve', None, None, 'endPath')
As you can see, pen objects are handy tools to get to the glyph data. The FontPens package contains a couple of pens which modify the contour when it is being drawn.
FlattenPen and flattenGlyph()
Process the contours into a series of straight lines by flattening the curves. That means that a curve is drawn as a series of straight lines, approximating the curve. The length of the segment (and therefore the number of segments) determines the precision. The pen can be controlled to flatten with different lengths.
FlattenPen works slightly differently from the PostScript “flattenpath” operator: flattenpath slices a cubic curve a fixed number of times, causing the segments to be of different lengths. The
FlattenPen measures each segment and tries to get them all the same length.
In order to make working with the
flattenPen also has a convenience function
flattenGlyph(). This takes care of the one-pen-controlling-another thing, and replaces the current outlines with the filtered results:
from fontPens.flattenPen import flattenGlyph d = 10 flattenGlyph(CurrentGlyph(), d)
Different values for
d will result in different lengths for the segments drawn by the FlattenPen.
ThresholdPen and thresholdGlyph()
ThresholdPen only draws segments which are longer than a certain distance. This can be useful for filtering small unnecessary details from autotraced contours. Obviously this is not a replacement for a trained eye, more like a vacuum cleaner for points:
from fontPens.thresholdPen import thresholdGlyph d = 10 thresholdGlyph(CurrentGlyph(), d)