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 lineto, moveto, curveto etc.) which are called by the glyph in the right order and with the right coordinates.

Pens used to live in RoboFab. With the development of FontParts, RoboFab pens were reorganized and moved to other packages. Some pens now live in ufoLib, some in fontPens, and some in fontTools.

See RoboFab vs. FontParts APIs > Pens for a complete list of RoboFab pens and their new locations.

Abstraction

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 objects work very different on the inside:

  • fontParts.nonelab.RGlyph stores the coordinates itself and writes to GLIF
  • mojo.roboFont.RGlyph stores data in RoboFont
  • robofab.objects.objectsRF.RGlyph stores data in FontLab
  • etc.

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.

Why pens?

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, Pen and PointsPen, which do different things for different purposes, and are intended for different methods in RGlyph.

Pen

The normal 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)
Move the pen to the (x, y) in pt.
lineTo(pt)
Draw a straight line to the (x, y) coordinate in pt.
curveTo(pt1, pt2, pt3)
Draw a classic Cubic Bezier (“PostScript”) curve through pt1 (offcurve), pt2 (also offcurve) and pt3 which 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. If the last point is set to None, no on curve is needed, the implied start point lays inbetween the first and last off curve.
closePath
Tell the pen the path is finished.
addComponent(baseName, transform)
Tell the pen to add a component of baseName, with a trasformation matrix transform: a 6 tuple containing an affine transformation (xx, xy, yx, yy, x, y).

PointsPen

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.

The PointsPen is passed to the aGlyph.drawPoints(aPointsPen).

beginPath(identifier=None)
Start a new sub path.
endPath
End the current sub path.
addPoint(pt, segmentType=None, smooth=False, name=None, **kwargs)
Add a point to the current sub path.
addComponent(self, baseName, transform)
Add a component of baseName, with a trasformation matrix transform: a 6 tuple containing an affine transformation (xx, xy, yx, yy, x, y).

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.

In RoboFont

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.


Adapted from the RoboFab documentation.

Last edited on 01/09/2021