A collection of simple scripts to do things to fonts.

Set fixed width in all glyphs

'''Set a fixed width to all glyphs in the current font.'''

# get the current font
font = CurrentFont()

# iterate over all glyphs in the font
for glyph in font:

    # set glyph width
    glyph.width = 400
'''Set a fixed width to all selected glyphs in the current font. Enable undo/redo.'''

# define a width value
value = 400

# get the current font
font = CurrentFont()

# iterate over all selected glyphs in the font
for glyph in font.selectedGlyphs:

    # set undo state
    glyph.prepareUndo()

    # set glyph width
    glyph.width = value

    # restore undo state
    glyph.performUndo()

Set infos in all open fonts

'''Set font infos from a dictionary into all open fonts.'''

# a dictionary with font infos
infoDict = {
    # attribute : data,
    'openTypeNameDesigner' : 'Claude Garamond',
    'year' : 1532,
    'copyright' : 'Copyright (c) 1532 Claude Garamond.',
    'note' : 'bonjour le monde',
}

# for each open font
for font in AllFonts():
    # iterate over all attributes and values
    for attr, value in infoDict.items():
        # set font info data
        setattr(font.info, attr, value)

# done!

Copy font infos from one font to another

'''Copy font info data from one open font to another.'''

# get the source font by name
f1 = AllFonts().getFontsByStyleName('Regular')[0]

# get the target font by name
f2 = AllFonts().getFontsByStyleName('Bold')[0]

# a list of font.info attributes to copy
attributes = [
    'familyName',
    'xHeight',
    'openTypeNameDesigner',
]

# copy font.info data from f1 to f2
for attr in attributes:
    # get value from source font
    value = getattr(f1.info, attr)
    # set value in target font
    setattr(f2.info, attr, value)

Batch generate fonts for all UFOs in folder

'''
Batch generate OTFs from a folder of UFOs.

1. open a dialog to select a folder
2. open each UFO in folder (without the UI)
3. generate OpenType-CFF font from UFO

'''

import os
from vanilla.dialogs import getFolder

# select folder
folder = getFolder('Select a folder with UFOs')[0]

# iterate over all files in folder
for f in os.listdir(folder):

    # skip files which are not .ufos
    if os.path.splitext(f)[-1] != '.ufo':
        continue

    # get full ufo path
    ufoPath = os.path.join(folder, f)

    # open ufo (without UI) # use 'showUI' in RF 1.8
    font = OpenFont(ufoPath, showInterface=False)

    # make otf path
    otfPath = ufoPath.replace('.ufo', '.otf')

    # generate otf font
    font.generate(path=otfPath, format='otf', decompose=True, checkOutlines=True, releaseMode=True)

    # close the ufo
    font.close()

Import a font into a layer of the current font

'''Import glyphs from a second font into a layer of the current font.'''

# get the current font
font1 = CurrentFont()

# open a second font (without the UI)
font2 = OpenFont(showInterface=False)

# create target layer
layerName = "%s %s" % (font2.info.familyName, font2.info.styleName)
dstLayer = font1.newLayer(layerName)

# loop over all glyphs in the second font
for glyph in font2:

    # add the glyph to the layer
    dstLayer[glyph.name] = glyph

# done!

Building accented glyphs

'''
Build accented glyphs in RoboFont3 using Glyph Construction.

'''

from glyphConstruction import ParseGlyphConstructionListFromString, GlyphConstructionBuilder

# define glyph constructions
txt = '''\
?agrave = a + grave@center,top
aacute = a + acute@center,top
'''

# get the actual glyph constructions from text
constructions = ParseGlyphConstructionListFromString(txt)

# get the current font
font = CurrentFont()

# collect glyphs to ignore if they already exist in the font
ignoreExisting = [L.split('=')[0].strip()[1:] for L in txt.split('\n') if L.startswith('?')]

# iterate over all glyph constructions
for construction in constructions:

    # build a construction glyph
    constructionGlyph = GlyphConstructionBuilder(construction, font)

    # if the construction for this glyph was preceded by `?`
    # and the glyph already exists in the font, skip it
    if constructionGlyph.name in font and constructionGlyph.name in ignoreExisting:
        continue

    # get the destination glyph in the font
    glyph = font.newGlyph(constructionGlyph.name, clear=True)

    # draw the construction glyph into the destination glyph
    constructionGlyph.draw(glyph.getPen())

    # copy construction glyph attributes to the destination glyph
    glyph.name = constructionGlyph.name
    glyph.unicode = constructionGlyph.unicode
    glyph.width = constructionGlyph.width
    glyph.markColor = 1, 1, 0, 0.5

    # if no unicode was given, try to set it automatically
    if glyph.unicode is None:
        glyph.autoUnicodes()
'''
Build accented glyphs in RoboFont1 using RoboFab’s font.compileGlyph.

'''

# get current font
font = CurrentFont()

# a dictionary of glyph constructions
accentsDict = {
    # accented glyph : [base glyph, [(accent, anchor)]],
    'agrave' : ['a', [('grave', 'top')]],
    'aacute' : ['a', [('acute', 'top')]],
    # ...add more accented glyphs here...
}

# iterate over all accented glyphs
for accentedGlyph in accentsDict.keys():

    # get base glyph and accents/anchors
    baseGlyph, accents = accentsDict[accentedGlyph]

    # build accented glyph using components
    font.compileGlyph(accentedGlyph, baseGlyph, accents)

Scale a font

'''Scale a font and what's inside it.'''

font = CurrentFont()

newUpm = 100
oldUpm = font.info.unitsPerEm
factor = newUpm / oldUpm

for layer in font.layers:

    for glyph in layer:

        for contour in glyph:
            contour.scaleBy(factor)

        for anchor in glyph.anchors:
            anchor.scaleBy(factor)

        for guideline in glyph.guidelines:
            guideline.scaleBy(factor)

        glyph.width *= factor

font.kerning.scaleBy(factor)

for guideline in font.guidelines:
    guideline.scaleBy(factor)

for attr in ['unitsPerEm', 'descender', 'xHeight', 'capHeight', 'ascender']:
    oldValue = getattr(font.info, attr)
    newValue = oldValue * factor
    setattr(font.info, attr, newValue)

font.changed()
Last edited on 24/10/2019