Pens ↩
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 GLIFmojo.roboFont.RGlyph
stores data in RoboFontrobofab.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)
inpt
. - lineTo(pt)
- Draw a straight line to the
(x, y)
coordinate inpt
. - curveTo(pt1, pt2, pt3)
- Draw a classic Cubic Bezier (“PostScript”) curve through
pt1
(offcurve),pt2
(also offcurve) andpt3
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 matrixtransform
: 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 matrixtransform
: 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.