recordingPen

Pen recording operations that can be accessed or replayed.

class fontTools.pens.recordingPen.DecomposingRecordingPen(glyphSet, *args, skipMissingComponents=None, reverseFlipped=False, **kwargs)[source]

Bases: DecomposingPen, RecordingPen

Same as RecordingPen, except that it doesn’t keep components as references, but draws them decomposed as regular contours.

The constructor takes a required ‘glyphSet’ positional argument, a dictionary of glyph objects (i.e. with a ‘draw’ method) keyed by thir name; other arguments are forwarded to the DecomposingPen’s constructor:

>>> class SimpleGlyph(object):
...     def draw(self, pen):
...         pen.moveTo((0, 0))
...         pen.curveTo((1, 1), (2, 2), (3, 3))
...         pen.closePath()
>>> class CompositeGlyph(object):
...     def draw(self, pen):
...         pen.addComponent('a', (1, 0, 0, 1, -1, 1))
>>> class MissingComponent(object):
...     def draw(self, pen):
...         pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
>>> class FlippedComponent(object):
...     def draw(self, pen):
...         pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
>>> glyphSet = {
...    'a': SimpleGlyph(),
...    'b': CompositeGlyph(),
...    'c': MissingComponent(),
...    'd': FlippedComponent(),
... }
>>> for name, glyph in sorted(glyphSet.items()):
...     pen = DecomposingRecordingPen(glyphSet)
...     try:
...         glyph.draw(pen)
...     except pen.MissingComponentError:
...         pass
...     print("{}: {}".format(name, pen.value))
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
c: []
d: [('moveTo', ((0, 0),)), ('curveTo', ((-1, 1), (-2, 2), (-3, 3))), ('closePath', ())]

>>> for name, glyph in sorted(glyphSet.items()):
...     pen = DecomposingRecordingPen(
...         glyphSet, skipMissingComponents=True, reverseFlipped=True,
...     )
...     glyph.draw(pen)
...     print("{}: {}".format(name, pen.value))
a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
c: []
d: [('moveTo', ((0, 0),)), ('lineTo', ((-3, 3),)), ('curveTo', ((-2, 2), (-1, 1), (0, 0))), ('closePath', ())]
exception MissingComponentError

Bases: KeyError

Indicates a component pointing to a non-existent glyph in the glyphset.

args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

addComponent(glyphName, transformation)

Transform the points of the base glyph and draw it onto self.

addVarComponent(glyphName, transformation, location)

Add a VarComponent sub glyph. The ‘transformation’ argument must be a DecomposedTransform from the fontTools.misc.transform module, and the ‘location’ argument must be a dictionary mapping axis tags to their locations.

closePath()

Close the current sub path. You must call either pen.closePath() or pen.endPath() after each sub path.

curveTo(*points)

Draw a cubic bezier with an arbitrary number of control points.

The last point specified is on-curve, all others are off-curve (control) points. If the number of control points is > 2, the segment is split into multiple bezier segments. This works like this:

Let n be the number of control points (which is the number of arguments to this call minus 1). If n==2, a plain vanilla cubic bezier is drawn. If n==1, we fall back to a quadratic segment and if n==0 we draw a straight line. It gets interesting when n>2: n-1 PostScript-style cubic segments will be drawn as if it were one curve. See decomposeSuperBezierSegment().

The conversion algorithm used for n>2 is inspired by NURB splines, and is conceptually equivalent to the TrueType “implied points” principle. See also decomposeQuadraticSegment().

draw(pen)
endPath()

End the current sub path, but don’t close it. You must call either pen.closePath() or pen.endPath() after each sub path.

lineTo(p1)

Draw a straight line from the current point to ‘pt’.

property log
moveTo(p0)

Begin a new sub path, set the current point to ‘pt’. You must end each sub path with a call to pen.closePath() or pen.endPath().

qCurveTo(*points)

Draw a whole string of quadratic curve segments.

The last point specified is on-curve, all others are off-curve points.

This method implements TrueType-style curves, breaking up curves using ‘implied points’: between each two consequtive off-curve points, there is one implied point exactly in the middle between them. See also decomposeQuadraticSegment().

The last argument (normally the on-curve point) may be None. This is to support contours that have NO on-curve points (a rarely seen feature of TrueType outlines).

replay(pen)
skipMissingComponents = False
class fontTools.pens.recordingPen.DecomposingRecordingPointPen(glyphSet, *args, skipMissingComponents=None, reverseFlipped=False, **kwargs)[source]

Bases: DecomposingPointPen, RecordingPointPen

Same as RecordingPointPen, except that it doesn’t keep components as references, but draws them decomposed as regular contours.

The constructor takes a required ‘glyphSet’ positional argument, a dictionary of pointPen-drawable glyph objects (i.e. with a ‘drawPoints’ method) keyed by thir name; other arguments are forwarded to the DecomposingPointPen’s constructor:

>>> from pprint import pprint
>>> class SimpleGlyph(object):
...     def drawPoints(self, pen):
...         pen.beginPath()
...         pen.addPoint((0, 0), "line")
...         pen.addPoint((1, 1))
...         pen.addPoint((2, 2))
...         pen.addPoint((3, 3), "curve")
...         pen.endPath()
>>> class CompositeGlyph(object):
...     def drawPoints(self, pen):
...         pen.addComponent('a', (1, 0, 0, 1, -1, 1))
>>> class MissingComponent(object):
...     def drawPoints(self, pen):
...         pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
>>> class FlippedComponent(object):
...     def drawPoints(self, pen):
...         pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
>>> glyphSet = {
...    'a': SimpleGlyph(),
...    'b': CompositeGlyph(),
...    'c': MissingComponent(),
...    'd': FlippedComponent(),
... }
>>> for name, glyph in sorted(glyphSet.items()):
...     pen = DecomposingRecordingPointPen(glyphSet)
...     try:
...         glyph.drawPoints(pen)
...     except pen.MissingComponentError:
...         pass
...     pprint({name: pen.value})
{'a': [('beginPath', (), {}),
       ('addPoint', ((0, 0), 'line', False, None), {}),
       ('addPoint', ((1, 1), None, False, None), {}),
       ('addPoint', ((2, 2), None, False, None), {}),
       ('addPoint', ((3, 3), 'curve', False, None), {}),
       ('endPath', (), {})]}
{'b': [('beginPath', (), {}),
       ('addPoint', ((-1, 1), 'line', False, None), {}),
       ('addPoint', ((0, 2), None, False, None), {}),
       ('addPoint', ((1, 3), None, False, None), {}),
       ('addPoint', ((2, 4), 'curve', False, None), {}),
       ('endPath', (), {})]}
{'c': []}
{'d': [('beginPath', (), {}),
       ('addPoint', ((0, 0), 'line', False, None), {}),
       ('addPoint', ((-1, 1), None, False, None), {}),
       ('addPoint', ((-2, 2), None, False, None), {}),
       ('addPoint', ((-3, 3), 'curve', False, None), {}),
       ('endPath', (), {})]}

>>> for name, glyph in sorted(glyphSet.items()):
...     pen = DecomposingRecordingPointPen(
...         glyphSet, skipMissingComponents=True, reverseFlipped=True,
...     )
...     glyph.drawPoints(pen)
...     pprint({name: pen.value})
{'a': [('beginPath', (), {}),
       ('addPoint', ((0, 0), 'line', False, None), {}),
       ('addPoint', ((1, 1), None, False, None), {}),
       ('addPoint', ((2, 2), None, False, None), {}),
       ('addPoint', ((3, 3), 'curve', False, None), {}),
       ('endPath', (), {})]}
{'b': [('beginPath', (), {}),
       ('addPoint', ((-1, 1), 'line', False, None), {}),
       ('addPoint', ((0, 2), None, False, None), {}),
       ('addPoint', ((1, 3), None, False, None), {}),
       ('addPoint', ((2, 4), 'curve', False, None), {}),
       ('endPath', (), {})]}
{'c': []}
{'d': [('beginPath', (), {}),
       ('addPoint', ((0, 0), 'curve', False, None), {}),
       ('addPoint', ((-3, 3), 'line', False, None), {}),
       ('addPoint', ((-2, 2), None, False, None), {}),
       ('addPoint', ((-1, 1), None, False, None), {}),
       ('endPath', (), {})]}
exception MissingComponentError

Bases: KeyError

Indicates a component pointing to a non-existent glyph in the glyphset.

args
with_traceback()

Exception.with_traceback(tb) – set self.__traceback__ to tb and return self.

addComponent(baseGlyphName, transformation, identifier=None, **kwargs)

Transform the points of the base glyph and draw it onto self.

The identifier parameter and any extra kwargs are ignored.

addPoint(pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs)

Add a point to the current sub path.

addVarComponent(baseGlyphName, transformation, location, identifier=None, **kwargs)

Add a VarComponent sub glyph. The ‘transformation’ argument must be a DecomposedTransform from the fontTools.misc.transform module, and the ‘location’ argument must be a dictionary mapping axis tags to their locations.

beginPath(identifier=None, **kwargs)

Start a new sub path.

drawPoints(pointPen)
endPath()

End the current sub path.

property log
replay(pointPen)
skipMissingComponents = False
class fontTools.pens.recordingPen.RecordingPen[source]

Bases: AbstractPen

Pen recording operations that can be accessed or replayed.

The recording can be accessed as pen.value; or replayed using pen.replay(otherPen).

Example:
from fontTools.ttLib import TTFont
from fontTools.pens.recordingPen import RecordingPen

glyph_name = 'dollar'
font_path = 'MyFont.otf'

font = TTFont(font_path)
glyphset = font.getGlyphSet()
glyph = glyphset[glyph_name]

pen = RecordingPen()
glyph.draw(pen)
print(pen.value)
addComponent(glyphName, transformation)[source]

Add a sub glyph. The ‘transformation’ argument must be a 6-tuple containing an affine transformation, or a Transform object from the fontTools.misc.transform module. More precisely: it should be a sequence containing 6 numbers.

addVarComponent(glyphName, transformation, location)[source]

Add a VarComponent sub glyph. The ‘transformation’ argument must be a DecomposedTransform from the fontTools.misc.transform module, and the ‘location’ argument must be a dictionary mapping axis tags to their locations.

closePath()[source]

Close the current sub path. You must call either pen.closePath() or pen.endPath() after each sub path.

curveTo(*points)[source]

Draw a cubic bezier with an arbitrary number of control points.

The last point specified is on-curve, all others are off-curve (control) points. If the number of control points is > 2, the segment is split into multiple bezier segments. This works like this:

Let n be the number of control points (which is the number of arguments to this call minus 1). If n==2, a plain vanilla cubic bezier is drawn. If n==1, we fall back to a quadratic segment and if n==0 we draw a straight line. It gets interesting when n>2: n-1 PostScript-style cubic segments will be drawn as if it were one curve. See decomposeSuperBezierSegment().

The conversion algorithm used for n>2 is inspired by NURB splines, and is conceptually equivalent to the TrueType “implied points” principle. See also decomposeQuadraticSegment().

draw(pen)
endPath()[source]

End the current sub path, but don’t close it. You must call either pen.closePath() or pen.endPath() after each sub path.

lineTo(p1)[source]

Draw a straight line from the current point to ‘pt’.

moveTo(p0)[source]

Begin a new sub path, set the current point to ‘pt’. You must end each sub path with a call to pen.closePath() or pen.endPath().

qCurveTo(*points)[source]

Draw a whole string of quadratic curve segments.

The last point specified is on-curve, all others are off-curve points.

This method implements TrueType-style curves, breaking up curves using ‘implied points’: between each two consequtive off-curve points, there is one implied point exactly in the middle between them. See also decomposeQuadraticSegment().

The last argument (normally the on-curve point) may be None. This is to support contours that have NO on-curve points (a rarely seen feature of TrueType outlines).

replay(pen)[source]
class fontTools.pens.recordingPen.RecordingPointPen[source]

Bases: AbstractPointPen

PointPen recording operations that can be accessed or replayed.

The recording can be accessed as pen.value; or replayed using pointPen.replay(otherPointPen).

Example:
from defcon import Font
from fontTools.pens.recordingPen import RecordingPointPen

glyph_name = 'a'
font_path = 'MyFont.ufo'

font = Font(font_path)
glyph = font[glyph_name]

pen = RecordingPointPen()
glyph.drawPoints(pen)
print(pen.value)

new_glyph = font.newGlyph('b')
pen.replay(new_glyph.getPointPen())
addComponent(baseGlyphName, transformation, identifier=None, **kwargs)[source]

Add a sub glyph.

addPoint(pt, segmentType=None, smooth=False, name=None, identifier=None, **kwargs)[source]

Add a point to the current sub path.

addVarComponent(baseGlyphName, transformation, location, identifier=None, **kwargs)[source]

Add a VarComponent sub glyph. The ‘transformation’ argument must be a DecomposedTransform from the fontTools.misc.transform module, and the ‘location’ argument must be a dictionary mapping axis tags to their locations.

beginPath(identifier=None, **kwargs)[source]

Start a new sub path.

drawPoints(pointPen)
endPath()[source]

End the current sub path.

replay(pointPen)[source]
fontTools.pens.recordingPen.lerpRecordings(recording1, recording2, factor=0.5)[source]

Linearly interpolate between two recordings. The recordings must be decomposed, i.e. they must not contain any components.

Factor is typically between 0 and 1. 0 means the first recording, 1 means the second recording, and 0.5 means the average of the two recordings. Other values are possible, and can be useful to extrapolate. Defaults to 0.5.

Returns a generator with the new recording.

fontTools.pens.recordingPen.replayRecording(recording, pen)[source]

Replay a recording, as produced by RecordingPen or DecomposingRecordingPen, to a pen.

Note that recording does not have to be produced by those pens. It can be any iterable of tuples of method name and tuple-of-arguments. Likewise, pen can be any objects receiving those method calls.