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.
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 (for instance the objectsFL.RGlyph and the objectsRF.RGlyph) work very different on the inside. One stores data in FontLab, the other stores the coordinates itself and writes to GLIF. But both 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 two 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 RoboFab glyphs can draw in the new world. But 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
RoboFab has two different kinds of pen which do different things for different purposes and they're intended for different methods in Glyph. Have a look in robofab/pens/ to see different kinds of pen objects for different purposes. RoboFab already has a fair number of pens in stock, chances are you'll find something you need. Examples of Penmanship at the How to use Pens.
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, smooth=False): move the pen to the (x, y) in pt.
- lineTo(pt, smooth=False): draw a straight line to the coordinate in pt (x, y).
- curveTo(pt1, pt2, pt3, smooth=False): 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.
- closePath(): tell the pen the path is finished.
- addComponent(baseName, offset=(0, 0), scale=(1, 1)): tell the pen to add a component of baseName, at offset and with scale.
- addAnchor(name, (x, y)): tell the pen to add an Anchor point with a name and a position.
- setWidth(width): tell the pen to set the width of the glyph. (deprecated)
- setNote(note): tell the pen to add a note to the glyph. (deprecated)
- doneDrawing(): 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 offcurve points. Again, have a look in the robofab/pens to see the available pens. Also the LettError wiki has an in-depth description of the pen protocols. The PointsPen is passed to the aGlyph.drawPoints(aPointsPen)
- beginPath(): 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, baseGlyphName, transformation): add a sub glyph.
Need a pen?
If you need a pen to do some drawing in a Glyph object, you can ask the glyph to get you one. Depending on the environment you're in RoboFab will get you the right kind of pen object to do the drawing.
# robofab manual
# Pen object
# usage examples
from robofab.world import CurrentGlyph
newGlyph = CurrentGlyph()
pen = newGlyph.getPen()
# do stuff with the pen to draw in this glyph
See also a more in depth look at pens here.