Adapted from the RoboFab documentation.
- Flavors of Pen
- Need a pen?
Pens are very useful and powerful objects. The idea is this: a glyph contains drawing information like contours, components etc. There is a lot of code that wants to work with this drawing information. But rather than to give access to these contours directly, the glyph and pen work together. Each glyph has a
draw() method which takes a pen as a parameter. The pen object has a couple of standard methods (along the lines of
curveto etc.) which are called by the glyph in the right order and with the right coordinates.
See RoboFab vs. FontParts APIs > Pens for a complete list of RoboFab pens and their new locations.
Using the pen as an intermediate, the code that just wants to draw a glyph doesn’t have to know the internal functions of the glyph, and in turn, the glyph doesn’t have to learn anything about specific drawing environments. Different kinds of glyph object work very different on the inside:
fontParts.nonelab.RGlyphstores the coordinates itself and writes to GLIF
mojo.roboFont.RGlyphstores data in RoboFont
robofab.objects.objectsRF.RGlyphstores data in FontLab
Despite the differences, all
RGlyph objects have a draw method which follows the same abstract drawing procedures. So the code that uses the
RGlyph.draw(pen) is not aware of the difference between the three kinds of glyphs.
In order to make a glyph draw in for instance a new graphics environment, you only need to write a new pen and implement the standard methods for the specifics of the environment. When that’s done, all glyphs can draw in the new world.
Pens have also proven to be very useful as a means to get access to the outline data stored in a glyph without messing with the internal workings of a glyph. So even if you don’t want to actually draw something on screen, the pen and
draw() interface can help in for instance conversion, transformations, etc. One glyph can draw itself into another glyph as a way of copying itself, while avoiding nasty dependencies and circular references.
Flavors of Pen
There are basically two different kinds of pen,
PointsPen, which do different things for different purposes, and are intended for different methods in
Pen object and pen that descend from it can be passed to
aGlyph.draw(aPen). The glyph calls these methods of the pen object to draw. It’s very similar to “Drawing like PostScript”.
- moveTo(pt, smooth=False)
- Move the pen to the
- lineTo(pt, smooth=False)
- Draw a straight line to the
(x, y)coordinate in
- curveTo(pt1, pt2, pt3, smooth=False)
- Draw a classic Cubic Bezier (“PostScript”) curve through
pt2(also offcurve) and
pt3which is oncurve again.
- qCurveTo(*pts, **kwargs)
- Draw a Quadratic (“TrueType”) curve through, well, any number of offcurve points. This is not the place to discuss Quadratic esoterics, but at least: this pen can deal with them and draw them.
- Tell the pen the path is finished.
- addComponent(baseName, offset=(0, 0), scale=(1, 1))
- Tell the pen to add a component of
- addAnchor(name, (x, y))
- Tell the pen to add an Anchor point with a name and a position.
- Tell the pen to set the width of the glyph. (deprecated)
- Tell the pen to add a note to the glyph. (deprecated)
- Tell the pen the drawing is done.
Where the normal pen is an easy tool to think about drawing, the
PointsPen is geared towards accessing all the data in the contours of the glyph. A
PointsPen has a very simple interface, it just steps through all the points in a Glyph. Too complicated if you just want your script to draw in a glyph somewhere, but very useful for conversions of one thing to another, and when you’re dealing with more elaborate point structures like several consecutive off-curve points.
PointsPen is passed to the
- Start a new sub path.
- End the current sub path.
- addPoint(pt, segmentType=None, smooth=False, name=None, **kwargs)
- Add a point to the current sub path.
- addComponent(self, baseGlyphName, transformation)
- Add a sub glyph.
Need a pen?
If you need a pen to do some drawing in a
RGlyph object, you can ask the glyph to get you one. Depending on the environment you’re in, the
RGlyph will get you the right kind of pen object to do the drawing.
newGlyph = CurrentGlyph() pen = newGlyph.getPen()
In NoneLab using FontParts
from fontParts.nonelab import RGlyph newGlyph = RGlyph() pen = newGlyph.getPen()
In FontLab using RoboFab
from robofab.world import CurrentGlyph newGlyph = CurrentGlyph() pen = newGlyph.getPen()
For a more in-depth look at pens, see Using pens.