| lettersoup | About »
Concepts |
OverviewTraditional font file formats - PS Type 1, TrueType, OpenType - have been historically constrained to simple point-by-point representations of glyphs. While this approach apparently makes things simpler, it restricts one's creativity to their own skills in point manipulation. These formats, and the software packages that operate on them, offer no straightforward way to apply changes such as "make the serifs wider by 2 points" or "make the shapes a little rounder" without going through a cumbersome process of editing every single glyph. Modern software packages, such as FontLab Studio, offer a few shortcuts for this purpose, but they're still inevitably bound to the point-by-point design paradigm. Furthermore, type design software and file formats have historically been closed, proprietary and thus solely oriented towards a technically-savvy group of specialists. Lettersoup is an attempt to propose a different way to design and manipulate typefaces, away from the conventions of traditional type design software. Some principles would constitute the foundation of such a system:
The Lettersoup model inevitably has more demands than regular font file formats, since it has to account for the dynamic rules to generate the final lettershapes. The file format thus has to go beyond a simple description of vector data in order to support variable parameters. Font master structureA font master is no more than a drawing machine that takes user input and returns a font according to the specified values. The creation process consists of
The Lettersoup font master file structure places each different step of the generation process into its own file:
VariablesLettersoup's model is based on having a fixed set of variables which are then fed to the font master script, which in turn builds the final glyph set according to the values specified. The reference for these is Donald Knuth's METAFONT; while the sample Lettersoup fontmaster only uses a few of them, it is perfectly feasible to build a fontmaster that takes advantage of having more tweakable parameters. The variables supported by the sample font-master are
These variables can be fed to Lettersoup via a variables.yaml file or directly through a GUI like the lettersoup-demo.py script. The way variables define the final lettershapes is defined in the parts file, the structure of which is explained below. BlueprintThe blueprint file specifies how Lettersoup should draw each glyph by indicating which parts go into it, as well as how they are drawn. For illustration purposes, we'll draw on the letter "H" from the sample fontmaster. h:
- part_stem:
pos: left
serifs: NW,NE,SW,SE
- part_stem:
pos: right
serifs: NW,NE,SW,SE
- part_bar:
start: left
end: right
The blueprint.yaml file, as its extension implies, is written in YAML, a markup language which blends CSS-style syntax with Python indentation rules, resulting in highly readable data structures. Let's now dissect the composition of this letter:
Next we'll look over how each of these parts is defined, as well as the attributes it accepts and how it interacts with the user variables. PartsThe parts file - parts.py - is a Shoebot script which defines how the common letter components - stems, bars, corners, diagonals, serifs - should be drawn. This file is written in the Shoebot/Nodebox domain-specific language, which is essentially Python code with a few vector drawing functions thrown in. Each part is defined in how it articulates with the variables specified above (em_width, cap_height, etc.). As an example, here's the code for generating the stem of the sample fontmaster in Lettersoup. It starts with a regular Python function declaration specifying the part name and the creation parameters it takes: def part_stem(serifs='NW,NE,SW,SE', pos=LEFT, start=None, end=None): The name of this part is part_stem - every part name has a "part-" prefix in order to avoid name collisions. The parameters it accepts is "serifs", "pos", "start" and "end". These are specified in the blueprint file to determine the final shape and position of each part. The function docstring specifies the necessary syntax: '''Vertical stem, used for example in I or H.
pos: can be LEFT, RIGHT or CENTER.
serifs: a comma-separated string, can include NW, NE, SW and/or SE.
Without further parameters, the stem will go from the top of the glyph
to the baseline. In other cases, such as the side of the O, you can
specify the start and end parameters:
start: can be
- TOPCORNER (starts right below the top corner height)
- BARCORNER (starts below the bar corner height, e.g. the
straight part of the B lower belly)
- or MIDDLE (starts at the middle of the glyph, depending on bar height)
If this is not specified, the stem starts at the top of the bounding box.
end: can be BARCORNER or BOTTOMCORNER. If this is not specified, the stem
ends at the bottom of the bounding box.
'''
Following that we have the actual code. The final shape is defined using Shoebot commands. In the sample font's case, the body of the stem is a rectangle, so we have to determine its coordinates according to the input parameters. # the stem body is a rectangle, so we start by defining the necessary
# coordinates to draw it (starting point x and y, width and height)
# set horizontal starting point according to position
if pos == LEFT:
x = jut
elif pos == RIGHT:
x = _glyph_width - jut - stem
elif pos == CENTER:
x = _glyph_width / 2 - stem/2
# set vertical starting point according to position
if start == TOPCORNER:
y = bar + gap
elif start == BARCORNER:
y = cap_height/2 + gap + bar_height
elif start == MIDDLE:
y = cap_height/2 + bar_height
else:
y = 0
# the width equals the stem variable
w = stem
if end == MIDDLE:
h = cap_height / 2 + bar/2 + bar_height
elif end == BOTTOMCORNER:
h = cap_height - bar - gap - y
elif end == BARCORNER:
h = cap_height/2 - gap - y + bar_height
else:
h = cap_height - y
# finally, draw the stem body
rect(x,y,w,h)
Afterwards, we take care of the serifs which, conveniently, are rectangles as well. # if serifs were specified, draw them
if serifs:
# make a list out of the input string
# which can be something like 'NW,NE'
serifs = serifs.split(',')
# NW serif
if NW in serifs:
x = stem_x - stem/2 - jut
y = 0
w = stem/2 + jut
h = slab
rect(x, y, w, h)
# NE serif
if NE in serifs:
x = stem_x
y = 0
w = stem/2 + jut
h = slab
rect(x, y, w, h)
# SW serif
if SW in serifs:
x = stem_x - stem/2 - jut
y = cap_height - slab
w = stem/2 + jut
h = slab
rect(x, y, w, h)
# SE serif
if SE in serifs:
x = stem_x
y = cap_height - slab
w = stem/2 + jut
h = slab
rect(x, y, w, h)
(NOTE: There are some helper variables, e.g. 'gap' and 'stem_x' which have to be replaced for clarity) WidthsFinally, we have the widths.yaml file. Its purpose is to simply store the width values for each glyph. It is also written in YAML, and looks like this: J: .55
K: .85
L: .65
M: 1.
N: .7
O: .65
The values are specified in relation to the em-width (note that the 'M' has a width of one). Generating a fontTODO: All the examples I used for my MA were quick hacks; the most necessary task now is to write a simple general-purpose script to generate font faces (all parts are working, we just need a simple interface). |
| Page last modified on August 29, 2008, at 11:47 AM |