<< . .

. 37
( : 45)

. . >>

logosl10.mf, which says simply
% 10-point slanted METAFONT logo
slant := 1/4;
input logo10
The slant parameter a¬ects currenttransform as explained in Chapter 15.
There isn™t a great deal of “meta-ness” in the logo.mf design, because
only a few forms of the logo are needed. However, some interesting
variations are possible; for example, if we use the parameter ¬les
font_size 30pt#; font_size 10pt#;
ht#:=25pt#; ht#:=6pt#;
xgap#:=1.5pt#; xgap#:=2pt#;
u#:=3/9pt#; u#:=4/3pt#;
s#:=1/3pt#; s#:=-2/3pt#;
o#:=2/9pt#; o#:=1/9pt#;
px#:=1pt#; px#:=1/3pt#;

we get and , respectively.
302 Appendix E: Examples

% Routines for the METAFONT logo, as found in The METAFONTbook
% (logo10.mf is a typical parameter file)
if unknown slant: slant:=0 else: currenttransform:=
identity slanted slant yscaled aspect_ratio fi;
ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment
ho#:=o#; % horizontal overshoot
leftstemloc#:=2.5u#+s#; % position of left stem
barheight#:=.45ht#; % height of bar lines
py#:=.9px#; % vertical pen thickness
pickup pencircle xscaled px yscaled py;
define_good_y_pixels(barheight); (Figure Eb will be inserted here;
define_corrected_pixels(o); too bad you can™t see it now.)

def beginlogochar(expr code, unit_width) =
pickup logo_pen enddef;
def super_half(suffix i,j,k) =
draw z.i{0,y.j-y.i}
... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
... z.j{x.k-x.i,0} (Figure A18a will be inserted here;
too bad you can™t see it now.)
... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
... z.k{0,y.k-y.j} enddef;
x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;
y1=y5; y2=y4; bot y1=-o;
top y2=h+o; y3=y1+ygap;
draw z1--z2--z3--z4--z5;
labels(1,2,3,4,5); endchar;
(Figure Ea will be inserted here;
beginlogochar("E",14); too bad you can™t see it now.)
x4=x6=w-x1+ho; x5=x4-xgap;
y1=y6; y2=y5; y3=y4;
bot y1=0; top y3=h; y2=barheight;
Appendix E: Examples 303

draw z6--z1--z3--z4; draw z2--z5;
labels(1,2,3,4,5,6); endchar;
italcorr ht#*slant + .5u#;
(Figure 18a will be inserted here; too
if .5w<>good.x .5w: change_width; fi
bad you can™t see it now.)
lft x1=-eps; x2=w-x1; x3=x4=.5w;
y1=y2=y3; top y1=h; bot y4=-o;
draw z1--z2; draw z3--z4;
labels(1,2,3,4); endchar;
x1=.5w; x2=x4=leftstemloc; x3=x5=w-x2;
top y1=h+o; y2=y3=barheight;
bot y4=bot y5=-o;
draw z4--z2--z3--z5; super_half(2,1,3); (Figure 4c will be inserted here; too bad you

labels(1,2,3,4,5); endchar; can™t see it now.)

x4=w-x1+ho; x5=x4-xgap;
y2=y5; y3=y4; bot y1=-o;
top y3=h; y2=barheight;
draw z1--z3--z4; draw z2--z5;
labels(1,2,3,4,5); endchar;
beginlogochar("O",15); (Figure 11a will be inserted here;
x1=x4=.5w; top y1=h+o; bot y4=-o; too bad you can™t see it now.)

x2=w-x3=good.x(1.5u+s); y2=y3=barheight;
super_half(2,1,3); super_half(2,4,3);
labels(1,2,3,4); endchar;
x1=x2=leftstemloc; x3=x4=x5=w-x1;
bot y1=bot y4=-o;
top y2=top y5=h+o; y3=y4+ygap;
draw z1--z2--z3; draw z4--z5;
(Figure 21a will be inserted here; too
labels(1,2,3,4,5); endchar; bad you can™t see it now.)

ligtable "T": "A" kern -.5u#;
ligtable "F": "O" kern -u#;
font_identifier:="MFLOGO" if slant<>0: & "SL" fi;
font_coding_scheme:="AEFMNOT only";
304 Appendix E: Examples

Everything in logo.mf has already been explained previously in this font identi¬er
font coding scheme
book except for the very last two lines, which de¬ne a ˜font identi¬er™ and italic correction
a ˜font coding scheme™. These are optional bits of information that are dis- Knuth
Computer Modern
cussed in Appendix F. Furthermore an italic correction has been speci¬ed for parameter ¬les
the letter ˜ ™, since it™s the ¬nal letter of ˜ ™. driver ¬les
program ¬les
base ¬le
The program for a complete typeface will di¬er from the program for this
simple logo font primarily in degree; there will be lots more parameters, lots more
subroutines, lots more characters, lots more ligatures and kerns and whatnot. But
there will probably also be more administrative machinery, designed to facilitate the
creation, testing, and modi¬cation of characters, since a large enterprise requires good
organization. The remainder of this appendix is devoted to an example of how this
might be done: We shall discuss the additional kinds of routines that the author found
helpful while he was developing the Computer Modern family of typefaces.
The complete, unexpurgated programs for Computer Modern appear in Com-
puters & Typesetting, Volume E; but since they have evolved over a long period of
time, they are rather complex. We shall simplify the details so that it will be easier to
grasp the important issues without being distracted by irrelevant technicalities.
The simple logo fonts discussed above are generated by two types of ¬les:
There are parameter ¬les like logo10.mf, and there is a program ¬le logo.mf. The
Computer Modern fonts, being more extensive, are generated by four types of ¬les:
There are parameter ¬les like ˜cmr10.mf™, which specify the ad hoc dimensions for par-
ticular sizes and styles of type; there are driver ¬les like ˜roman.mf™, which serve as chief
executives of the font-generation process; there are program ¬les like ˜punct.mf™, which
contain programs for individual characters; and there™s a base ¬le called ˜cmbase.mf™,
which contains the subroutines and other macros used throughout the system.
Our logo example could have been cast in this more general mold by moving
the character programs into a program ¬le ˜METAFON.mf™, and by moving most of the
opening material into a base ¬le ˜logobase.mf™ that looks like this:

% Base file for the METAFONT logo
logobase:=1; % when logobase is known, this file has been input
def font_setup =
if unknown slant: slant:=0 else: currenttransform:=
. (the previous code is unchanged)
define_horizontal_corrected_pixels(ho); enddef;
followed by the de¬nitions of beginlogochar and super_half. Then we™re left with a
driver ¬le logo.mf that looks like this:

% Driver file for the METAFONT logo
if unknown logobase: input logobase fi
mode_setup; font_setup; % establish pixel-oriented units
input METAFON % generate the characters
ligtable "T": "A" kern -.5u#;
and so on, concluding as before.
Appendix E: Examples 305

In general, a parameter ¬le calls on a driver ¬le, which calls on one or more cmr10.mf
program ¬les; the base ¬le contains prede¬ned macros shared by all. There may be
several driver ¬les, each using a di¬erent combination of program ¬les; for example, end
Computer Modern has ˜roman.mf™ and ˜italic.mf™, both of which call on punct.mf to font slant
font quad
generate punctuation marks, although they use di¬erent program ¬les to generate the font normal space
lowercase alphabets. Characters are partitioned into program ¬les so that they can be font normal stretch
font normal shrink
shared by di¬erent drivers.
Parameter ¬les in Computer Modern don™t quite follow the conventions of
logo10.mf. Here, for example, are the opening and closing lines of cmr10.mf:
% Computer Modern Roman 10 point
if unknown cmbase: input cmbase fi
font_identifier "CMR"; font_size 10pt#;
u#:=20/36pt#; % unit width
serif_fit:=0pt#; % extra sidebar near serifs
letter_fit:=0pt#; % extra space added to all sidebars
serifs:=true; % should serifs and bulbs be attached?
monospace:=false; % should all characters have the same width?
generate roman % switch to the driver file
The main di¬erences are: (1) There™s special code at the beginning, to make sure that
cmbase.mf has been loaded. The base ¬le includes several things that are needed right
away; for example, cmbase declares the variables ˜serifs ™ and ˜monospace ™ to be of type
boolean, so that boolean-valued parameter assignments like ˜serifs := true™ will be
legal. (2) The font identi¬er is de¬ned in the parameter ¬le, not in the driver ¬le.
(3) The last line says ˜generate™ instead of ˜input™; the base ¬le de¬nes generate to
be the same as input, but other meanings are assigned by utility routines that we™ll
study later. (4) The ¬nal ˜end™ is no longer present in the parameter ¬le.
The roman.mf driver looks like this (vastly simpli¬ed):
% The Computer Modern Roman family of fonts
mode_setup; font_setup;
input romanu; % upper case (majuscules)
input romanl; % lower case (minuscules)
input romand; % numerals
input punct; % punctuation marks
font_slant slant;
if monospace: font_quad 18u#;
font_normal_space 9u#; % no stretching or shrinking
else: font_quad 18u#+4letter_fit#;
font_normal_space 6u#+2letter_fit#; % interword spacing
font_normal_stretch 3u#; % with ˜˜glue™™
font_normal_shrink 2u#;
input romlig; % f ligatures
ligtable "f": "i" =: oct"014", "f" =: oct"013", "l" =: oct"015",
"™" kern u#, "?" kern u#, "!" kern u#;
306 Appendix E: Examples

ligtable oct"013": "i" =: oct"016", "l" =: oct"016", % ffi and ffl bye.
"™" kern u#, "?" kern u#, "!" kern u#;
ligtable "-": "-" =: oct"173"; % en dash .
em dash
ligtable oct"173": "-" =: oct"174"; % em dash
punctuation marks
ligtable "˜": "˜" =: oct"134"; % open quotes
ligtable "™": "™" =: oct"042", % close quotes
"?" kern 2u#, "!" kern 2u#;
fi; bye.
In a monospaced font like cmtt10, all characters will be exactly 9u# wide. Both cmr10
and cmtt10 use the roman driver, but roman omits the ligatures and changes the inter-
word spacing when it is producing monospaced fonts.
The program ¬les of Computer Modern have slightly di¬erent conventions
from those of plain . Here, for example, are the programs for two of the
simplest punctuation marks:
cmchar "Period";
numeric dot_diam#; dot_diam# = if monospace: 5/4 fi dot_size#;
adjust_fit(0,0); pickup fine.nib;
pos1(dot_diam,0); pos2(dot_diam,90);
x1l=good.x(x1l+.5w-x1); bot y2l=0; z1=z2; dot(1,2); % dot
penlabels(1,2); endchar;

(Figure Ec&Ed will be inserted here; too bad you can™t see it now.)

iff not monospace: cmchar "Em dash";
italcorr .61803x_height#*slant + .5u#;
pickup crisp.nib; pos1(vair,90); pos2(vair,90);
y1r=y2r=good.y(y1r+.61803h-y1); lft x1=-eps; rt x2=w+eps;
filldraw stroke z1e--z2e; % crossbar
penlabels(1,2); endchar;
Appendix E: Examples 307

The new structural features in these programs are: (1) ˜cmchar™, which appears at cmchar

the very beginning of each character program; (2) ˜i¬ boolean expression :™, which
adjust ¬t
precedes cmchar if the character is to be generated only when the boolean expression relax
is true; (3) ˜adjust ¬t™, which can change the amount of white space at the character™s always if
left and/or right; (4) pens called ˜¬ne.nib ™ and ˜crisp.nib ™; (5) new macros ˜pos ™, ˜dot ™, outer
and ˜stroke ™, discussed further below. sidebearings
bounding box
The base ¬le cmbase.mf begins as follows:
adjust ¬t
letter ¬t
% The base file for Computer Modern (a supplement to plain.mf)
cmbase:=1; % when cmbase is known, this file has been input
let cmchar = relax; % ˜cmchar™ should precede each character
let generate = input; % ˜generate™ should follow the parameters
newinternal slant, superness, · · · % purely numeric parameters
boolean serifs, monospace, · · · % boolean parameters
These few lines are straightforward enough. Although cmchar is de¬ned to be the
same as relax, which does nothing, the de¬nition of cmchar will be changed by certain
utility programs below; this will prove to be a convenience when characters are designed,
tested, and maintained.
The next few lines of cmbase are trickier. They implement the ˜i¬ ™ feature,
which bypasses unwanted characters at high speed.
let semi_ = ;; let colon_ = :; let endchar_ = endchar;
def iff expr b =
if b: let next_ = use_it else: let next_ = lose_it fi;
next_ enddef;
def use_it = let : = restore_colon; enddef;
def restore_colon = let : = colon_; enddef;
def lose_it = let endchar = fi; inner cmchar; let ; = fix_ semi_
if false enddef;
def fix_ = let ; = semi_; let endchar = endchar_; outer cmchar; enddef;
def always_iff = let : = endgroup; killboolean enddef;
def killboolean text t = use_it enddef;
outer cmchar;
(The lose_it routine assumes that every character program will end with ˜endchar;™.)
The most interesting part of cmbase is probably the way it allows the “side-
bearings” of each character to be ¬ne-tuned. The amount of space at the left and right
edges of the character™s “bounding box” can be adjusted without actually shifting the
picture, and without changing the width that was speci¬ed in beginchar. Here™s
how it works: After a beginchar command and an optional italcorr, each Computer
Modern character program is supposed to say
adjust ¬t( left sidebearing adjustment , right sidebearing adjustment );
sidebearing adjustments are given in true, “sharped” units. The adjust ¬t routine
essentially adds extra space at the left and right, corresponding to the sidebearing
adjustments. An ad-hoc dimension called “letter ¬t #” is also added to all sidebearings,
behind the scenes.
308 Appendix E: Examples

Our example program for the "." says simply ˜adjust ¬t(0, 0)™; this means font quad
that only letter ¬t is added. The program for em-dash says ˜adjust ¬t(letter ¬t #, b
serif ¬t
letter ¬t #)™, hence the sidebearings are increased by 2letter ¬t at each side. The total sans-serif
character width of the em-dash comes to 18u# + 4letter ¬t # (which is indeed one em, monospace
shrink ¬t
the value of font quad speci¬ed in the roman driver ¬le). l
The program for lowercase ˜b™ in ¬le romanl.mf says ˜adjust ¬t(serif ¬t #, 0)™; r
this adds the serif ¬t parameter at the left, to compensate for the possible appearance
of a serif at the left of this character. The serif ¬t is zero in cmr10, but it has a negative u
value in a sans-serif font, and a positive value when serifs are extralong. jut
mono charwd
The nice thing about adjust ¬t is that it™s an “add-on” speci¬cation that
doesn™t a¬ect the rest of the character design. The program can still be written as
if 0 were the left edge and w were the right edge; afterwards the ¬t can be adjusted
without changing the program or the shapes.
There are two versions of adjust ¬t, one for normal fonts and one for mono-
space fonts. Both of them are slightly complicated by something called shrink ¬t , which
will be explained later; for the moment, let™s just imagine that shrink ¬t = 0. Here is
the routine for the normal case:
def normal_adjust_fit(expr left_adjustment,right_adjustment) =
l := -hround(left_adjustment*hppp)-letter_fit;
interim xoffset := -l;
charwd := charwd+2letter_fit#+left_adjustment+right_adjustment;
r := l+hround(charwd*hppp)-shrink_fit;
w := r-hround(right_adjustment*hppp)-letter_fit;
Variables l and r are set to the actual pixel boundaries of the character; thus, plain
™s bounding box has 0 ¤ x ¤ w, but Computer Modern™s has l ¤ x ¤ r.
Rounding has been done very carefully so that the sidebearings will have consistent
relationships across an entire font. Notice that w has been recalculated; this means
that adjust ¬t can a¬ect the digitization, but”we hope”in a bene¬cial way.
In a monospaced font, the adjust ¬t routine changes the unit-width param-
eter, u , so that the total width after adjustment comes out to be constant. Similar
adjustments are made to parameters like jut , the nominal serif length. The width of
all characters in a monospaced font will be mono charwd # in true units, mono charwd
in pixels. The italic correction of all characters will be mono charic #.
def mono_adjust_fit(expr left_adjustment,right_adjustment) =
numeric expansion_factor; mono_charwd# = 2letter_fit#
+ expansion_factor*(charwd+left_adjustment+right_adjustment);
forsuffixes $=u,jut, · · · :
$ := $.#*expansion_factor*hppp; endfor
l := -hround(left_adjustment*expansion_factor*hppp)-letter_fit;
interim xoffset := -l;
r := l+mono_charwd-shrink_fit;
w := r-hround(right_adjustment*expansion_factor*hppp)-letter_fit;
charwd := mono_charwd#; charic := mono_charic#;
It took the author umpteen trials to get this routine right.
Appendix E: Examples 309

The xo¬set calculations in adjust ¬t are enough to shift the character by the xo¬set
shipped out
proper amount when it™s being shipped out. We just have to take care of getting the
extra endchar
correct character width in pixels, and cmbase does this by setting endchar
extra_endchar := extra_endchar&"r:=r+shrink_fit;w:=r-l;"; maketicks
change width
No other changes to plain ™s endchar routine are needed; but we font setup
do need to rede¬ne makebox and maketicks, in order to show the adjusted bounding
box. It™s convenient to change makebox so that it also slants the box, in a slanted font,
and so that it draws vertical lines one unit apart as aids to the designer; several more
horizontal lines are also drawn:
def makebox(text rule) =
for y=0,asc_height,body_height,x_height,bar_height,
-desc_depth,-body_depth: rule((l,y)t_,(r,y)t_); endfor % horizontals
for x=l,r: rule((x,-body_depth)t_,(x,body_height)t_); endfor % verticals
for x=u*(1+floor(l/u)) step u until r-1:
rule((x,-body_depth)t_,(x,body_height)t_); endfor % more verticals
if charic<>0:
rule((r+charic*pt,h.o_),(r+charic*pt,.5h.o_)); fi % italic correction
def maketicks(text rule) =
for y=0,h.o_,-d.o_:
rule((l,y),(l+10,y)); rule((r-10,y),(r,y)); endfor % horizontals
for x=l,r: rule((x,10-d.o_),(x,-d.o_));
rule((x,h.o_-10),(x,h.o_)); endfor % verticals
if charic<>0:

<< . .

. 37
( : 45)

. . >>