Skip to content

Instantly share code, notes, and snippets.

@anthrotype
Created March 11, 2020 11:19
Show Gist options
  • Select an option

  • Save anthrotype/0f70ebc2143dfb01c25c47121e7e6814 to your computer and use it in GitHub Desktop.

Select an option

Save anthrotype/0f70ebc2143dfb01c25c47121e7e6814 to your computer and use it in GitHub Desktop.
from glyphsLib import GSFont
# 'offcurve' GSNode.type is equivalent to 'None' in UFO PointPen API
_UFO_NODE_TYPES = {"line", "curve", "qcurve"}
def drawGSLayerWithPointPen(layer, pointPen):
""" Draw a Glyphs layer with a FontTools PointPen.
Args:
layer: a glyphsLib.classes.GSLayer object
pointPen: a UFO point pen (cf. fontTools AbstractPointPen).
Source:
https://github.com/googlefonts/glyphsLib/blob/master/Lib/glyphsLib/builder/paths.py
https://github.com/googlefonts/glyphsLib/blob/master/Lib/glyphsLib/builder/components.py
"""
for path in layer.paths:
nodes = list(path.nodes)
pointPen.beginPath()
if not nodes:
pointPen.endPath()
continue
if not path.closed:
node = nodes.pop(0)
assert node.type == "line", "Open path starts with off-curve points"
pointPen.addPoint(tuple(node.position), segmentType="move")
else:
# In Glyphs.app, the starting node of a closed contour is always
# stored at the end of the nodes list.
nodes.insert(0, nodes.pop())
for node in nodes:
node_type = node.type if node.type in _UFO_NODE_TYPES else None
node_data = dict(node.userData)
node_name = node_data.pop("name", None)
pointPen.addPoint(
tuple(node.position),
segmentType=node_type,
smooth=node.smooth,
name=node_name,
userData=node_data,
)
pointPen.endPath()
for component in layer.components:
pointPen.addComponent(component.name, component.transform)
if __name__ == "__main__":
import sys
from pprint import pprint
from functools import partial
filename = sys.argv[1]
font = GSFont(filename)
# for master in font.masters...
master = next(iter(font.masters))
# for glyph in font.glyphs...
try:
glyph_name = sys.argv[2]
except IndexError:
glyph_name = "a"
glyph = font.glyphs[glyph_name]
if glyph is None:
sys.exit(f"glyph {glyph_name!r} is missing")
# get each glyph's master layer
layer = font.glyphs[glyph_name].layers[master.id]
# draw the layer with a point pen
drawPointsFunc = partial(drawGSLayerWithPointPen, layer)
# E.g. dump it to GLIF format
from fontTools.ufoLib.glifLib import writeGlyphToString
glif = writeGlyphToString(glyph_name, drawPointsFunc=drawPointsFunc)
print(glif)
# Or record it with a RecordingPointPen
from fontTools.pens.recordingPen import RecordingPointPen
rec = RecordingPointPen()
drawPointsFunc(rec)
pprint(rec.value)
# if you need segments, use PointToSegmentPen adapter pen
from fontTools.pens.recordingPen import RecordingPen
from fontTools.pens.pointPen import PointToSegmentPen
rec = RecordingPen()
pointPen = PointToSegmentPen(rec)
drawPointsFunc(pointPen)
pprint(rec.value)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment