The joy of build scripts

When writing software, there is a big difference between the human readable code, and the compiled machine readable code. The human readable code is easy to edit, while the machine readable code can only be read by complete psycos, and even there with lots of effort. In order to get from human to computer readable, there is a step called compiling.

I've found this to be a useful paradigm with type design as well. The source files are my FontLab files. There are a lot of things that need to happen between my easy to edit source files and the finished exported fonts. For one thing, I use interpolation for some of the weights. All of the interpolated weights must be created, then renamed with have the proper "Font Info" applied. Also, I often like to use separate contours and sometimes even components for different strokes (see fig. 1). For example, it's much easier to adjust the weight and correct italic distortions in the "A" if the crossbar isn't welded on to the frame. These all need to be decomposed with overlaps removed. When I started making fonts, it would take forever if I needed to make a tweak and re-export final files.

Enter the build script. When compiling complicated software, you will often see a file called Makefile, build.sh or build.xml in the directory with all of the source. These files are often complicated scripts telling the complier (whether it be java, c, or even Actionscript) where all of the pieces are and the steps to compile to final executable software.

I've started doing the same thing with my type design projects. Along with a series of master files in the project directory is a file called build.py. This build file is customized for each project. Here is the build script for Doublewide:

"build script for Doublewide"

from MixFonts import Mix,Master
from SetNames import setNames
from string import split
from ItalicizeGlyph import italicizeGlyph

BASEDIR = "/Volumes/Data/Type/2007/Doublewide"
BUILDDIR = "Build_Doublewide"
DECOMPOSELIST = split("A B D E F G H K L M N P Q R S T U V W X Z Y OE oe onesuperior tm section a b d e f g h i k l m n p q r t u z dollar registered plusminus notequal multiply ampersand Thorn thorn one eight AE ae yen sterling cent Oslash oslash eth Eth lslash Lslash plus numbersign fl fi at asterisk Ccedilla ccedilla Euro")

fl.Open("%s/Doublewide_Bold.vfb"%BASEDIR,False)
BOLD = fl[len(fl)-1]

fl.Open("%s/Doublewide_Light.vfb"%BASEDIR,False)
LIGHT = fl[len(fl)-1]

fl.Open("%s/Doublewide_Medium.vfb"%BASEDIR,False)
MEDIUM = fl[len(fl)-1]

fl.Open("%s/Doublewide_LightItalic.vfb"%BASEDIR,False)
LIGHTITALIC = fl[len(fl)-1]

fl.Open("%s/Doublewide_BoldItalic.vfb"%BASEDIR,False)
BOLDITALIC = fl[len(fl)-1]


def copyFeatures(f1, f2):
	for ft in f1.features:
		t = Feature(ft.tag, ft.value)
		f2.features.append(t)
	f2.ot_classes = f1.ot_classes

def makeFont(mix,names,italic = None):
	n = names.split("/")
	print n[0],n[1]
	f = mix.mixFonts()
	fl.Add(f)
	copyFeatures(BOLD,f)
	#fl.CallCommand(33553)
	for gname in DECOMPOSELIST:
		g = f[f.FindGlyph(gname)]
		if g != None:
			g.Decompose()
			g.RemoveOverlap()
	g.Autohint()
		
	index = len(fl)-1
	fl.UpdateFont(index)
	setNames(f,n[0],n[1],n[2],n[3])
	otfName = "%s/%s/otf/%s.otf"%(BASEDIR,BUILDDIR,f.font_name)
	ttfName = "%s/%s/ttf/%s.ttf"%(BASEDIR,BUILDDIR,f.font_name)
	fl.GenerateFont(index,ftOPENTYPE,otfName)
	fl.GenerateFont(index,ftTRUETYPE,ttfName)
	
	f.modified = 0
	fl.Close(index)

# Masters

lt = Master(LIGHT,0.)
bd = Master(BOLD,1.)

ltit = Master(LIGHTITALIC,0.)
bdit = Master(BOLDITALIC,1.)

#exceptions
med = Master(MEDIUM,.5)

makeFont(Mix([lt,bd],0.),"Doublewide/Light/Regular/Lt")
makeFont(Mix([lt,med,bd],Point(0.3,0.38)),"Doublewide/Regular/Regular/Rg")
makeFont(Mix([lt,med,bd],Point(0.56,0.61)),"Doublewide/Medium/Bold/Lt")
makeFont(Mix([lt,bd],1.),"Doublewide/Bold/Bold/Rg")

makeFont(Mix([ltit,bdit],Point(0.,-0.05)),"Doublewide/Light Italic/Italic/Lt")
makeFont(Mix([ltit,bdit],Point(0.3,0.30)),"Doublewide/Italic/Italic/Rg")
makeFont(Mix([ltit,bdit],Point(0.56,0.61)),"Doublewide/Medium Italic/Bold Italic/Lt")
makeFont(Mix([ltit,bdit],1.),"Doublewide/Bold Italic/Bold Italic/Rg")

Note: Mix and Master are my interpolation classes. More on them later.

If only I had come upon this earlier, I would not have always welded everything together. Thanks for sharing. :)

I used a modified version of this script and it was quite useful. I was just wondering if the Mix and Master classes were going to be discussed.

Thanks

I'm glad that you found the idea of build scripts for type design useful.

Mix is a class that contains tools for interpolating point compatible fonts and glyphs. There is very little error trapping. If a glyph isn't point compatible, it just inserts the glyph from the first master and marks it red.

The Master class is used to create masters for the Mix class. Masters can be created from either FontLab Font objects or Mix objects. This allows for more complicated interpolations where more than one font is used as an input.

I initially wrote these tools because I was dissatisfied with Multiple Masters for interpolation tasks. The specific problems with MM as a type development tool are outlined pretty well in the sales material for Superpolator. Maybe if Superpolator had come out earlier I might have avoided this process :)

I've been thinking about putting the code out there, but at this point the tools are extremely user unfriendly. It is also largely redundant to tools available in RoboFab. It would require a lot of documentation to make them useful to a wider audience (if you can call the Python writing FontLab set a wide audience :). If you are interested in the raw source, I'd be happy to send it over for you to play with. Just send me a message through the contact page.

Thanks for posting this information
_____________________
maine screen printing