This example generates a proof of all selected glyphs in the current font (or all glyphs, if no glyph is selected), one glyph per page.

Each page shows a single glyph in the center of the page, and additional data such as font metrics, glyph box, anchors, glyph name, unicode, width, and left/right margins.

Page size, glyph scale, caption color and caption size can be adjusted in the variables at the top of the script.

'''Glyph Proofer'''

f = CurrentFont()

# settings
glyphScale = 0.6
canvasWidth = canvasHeight = 850
captionSize = 13
captionColor = 1, 0, 0

# collect vertical metrics
metricsY = {
    0,
    f.info.descender,
    f.info.xHeight,
    f.info.capHeight,
    f.info.ascender,
}

# get box height
boxHeight = (max(metricsY) - min(metricsY)) * glyphScale
boxY = (canvasHeight - boxHeight) * 0.5

# get glyph names
glyphNames = f.selectedGlyphNames if len(f.selectedGlyphs) else f.keys()

# draw glyphs
for glyphName in f.glyphOrder:
    if not glyphName in glyphNames:
        continue

    # get glyph
    g = f[glyphName]
    boxWidth = g.width * glyphScale

    # make new page
    newPage(canvasWidth, canvasHeight)

    # calculate origin position
    x = (canvasWidth - boxWidth) * 0.5
    y = boxY + abs(f.info.descender) * glyphScale

    # collect horizontal metrics
    guidesX = {x, x + boxWidth}

    # --------
    # draw box
    # --------

    save()
    fill(0.9)
    rect(x, boxY, boxWidth, boxHeight)
    restore()

    # -----------
    # draw guides
    # -----------

    save()
    lineDash(6, 3)
    stroke(0.5)

    # draw guides x
    for guideX in guidesX:
        line((guideX, 0), (guideX, height()))

    # draw guides y
    for guideY in metricsY:
        guideY = y + guideY * glyphScale
        line((0, guideY), (width(), guideY))

    restore()

    # ----------
    # draw glyph
    # ----------

    save()
    fill(None)
    stroke(0)
    strokeWidth(5)
    lineJoin('round')
    translate(x, y)
    scale(glyphScale)
    drawGlyph(g)
    restore()

    # ------------
    # draw anchors
    # ------------

    radius = 10
    save()
    fill(None)
    stroke(1, 0, 0)
    translate(x, y)
    for anchor in g.anchors:
        aX, aY = anchor.position
        aX *= glyphScale
        aY *= glyphScale
        oval(aX - radius, aY - radius, radius * 2, radius * 2)
        line((aX - radius, aY), (aX + radius, aY))
        line((aX, aY - radius), (aX, aY + radius))
    restore()

    # ------------
    # draw caption
    # ------------

    captionX = captionSize
    captionW = width() - captionSize * 2
    captionH = captionSize * 2

    save()
    font('Menlo-Bold')
    fontSize(captionSize)
    fill(*captionColor)

    # top
    captionY = height() - captionSize * 3
    captionBox = captionX, captionY, captionW, captionH
    textBox(g.name, captionBox, align='left')
    if g.unicode:
        uni = str(hex(g.unicode)).replace("0x", '')
        uni = uni.zfill(4).upper()
        textBox(uni, captionBox, align='right')

    # bottom
    captionY = 0
    captionBox = captionX, captionY, captionW, captionH
    textBox('%.2f' % g.width, captionBox, align='center')
    if g.bounds:
        textBox('%.2f' % g.leftMargin, captionBox, align='left')
        textBox('%.2f' % g.rightMargin, captionBox, align='right')

    restore()

This example could be extended to display other types of data, such as on-curve and off-curve points, blue zones, etc.

Last edited on 01/09/2021