. 7
( 13)


Strings can be converted into numbers, although Chapter 8 didn™t mention
this fact in its syntax for numeric primary . The primitive operations are

ASCII string primary | oct string primary | hex string primary

where ˜ASCII™ returns the ASCII code of the ¬rst character of the string, ˜oct™ computes
an integer from a string representing octal notation (radix 8), and ˜hex™ computes an
integer from a string representing hexadecimal notation (radix 16). For example,

ASCII "100" = 49; oct "100" = 64; hex "100" = 256.

Several exceptional conditions need to be mentioned: (1) ASCII "" = ’1; otherwise
ASCII yields an integer between 0 and 255. (2) The characters in the string argument
to ˜oct™ must all be digits in the range 0“7. (3) The characters in the string argument
to ˜hex™ must all be digits in the range 0“9, A“F, or a“f. (4) The number that results
from ˜oct™ or ˜hex™ must be less than 4096. Thus, ˜oct "7777"™ and ˜hex "FFF"™ are the
maximum legal values.
Chapter 22: Strings 189

EXERCISE 22.1 message command
message command
Under what circumstances is (a) ASCII char n = n? (b) char ASCII s = s?
message op
EXERCISE 22.2 errmessage
Why are there primitive operations to convert from strings to numbers assum- errhelp
ing octal notation and hexadecimal notation, but not assuming decimal notation?
mode def
Write an octal macro that converts a nonnegative integer to an octal string. THOMSON

A message command allows you to communicate directly or indirectly with
the user. It has the general syntax
message command ’’ message op string expression
message op ’’ message | errmessage | errhelp
If you say ˜message s™, the characters of s will be typed on the terminal, at the
beginning of a new line; ˜errmessage s™ is similar, but the string will be preceded by
"! " and followed by ".", followed by lines of context as in ™s normal error
messages. If the user asks for help after an errmessage error, the most recent errhelp
string will be typed (unless it was empty).
doesn™t allow you to have an array of di¬erent macros m[i];
but you can have an array of strings that have macro-like behavior, via
scantokens. The mode def construction of Appendix B exploits this idea.

Many other useful Practises
mecanicks perform by this Theo.
as the ¬nding the length of strings.
” WILLIAM ALINGHAM, Geometry Epitomized (1695)

Forgive me, if my trembling Pen displays
What never yet was sung in mortal Lays.
But how shall I attempt such arduous String?
” JAMES THOMSON, The Castle of Indolence (1748)
(page 190)

Chapter 23: Online Displays 191

How do you get pictures to appear on your screen? Plain provides showit
the ˜showit™ command, which displays the currentpicture . Furthermore you can screenchars
ask for ˜screenchars™; this automatically does a showit at the time of each endchar
endchar. And you can see all the action by asking for ˜screenstrokes™; this display command
automatically does a showit after every draw or ¬ll. display
The above-described features of plain are implemented from low-
openwindow command
level primitive commands, by macros that appear in Appendix B. At the low- openwindow
obeys commands such as ˜display currentpicture inwindow 1™;
est level, window spec
there™s also an ˜openwindow™ command that de¬nes a correspondence between -
screen place
coordinates and screen coordinates. The syntax is from
display command ’’ display picture variable inwindow window screen coordinates
window ’’ numeric expression Cartesian
openwindow command ’’ openwindow window window spec
window spec ’’ screen place at pair expression
screen place ’’ from screen coordinates to screen coordinates
screen coordinates ’’ pair expression
A window is an integer between 0 and 15, inclusive; it represents one of sixteen “win-
dows” or “portholes” that provides between its pictures and the outside
world. The window mentioned in a display command must previously have been
“opened” by an openwindow command.
™s windows should not be confused with the so-called windows
provided by many modern operating systems. If you have such a system,
you™ll probably ¬nd that all of ™s pictorial output appears in one operating-
system window, and all of its terminal I/O appears in another, and you might be
running other jobs (like the system editor) in another. ™s windows are not
so fancy as this; they are just internal subwindows of one big picture window.
The command ˜openwindow k from (r0 , c0 ) to (r1 , c1 ) at (x, y)™ associates a
rectangular area of the user™s screen (or of the user™s big picture window) with
pixels in ™s coordinate system. All of the numbers in this command (namely
k, r0 , c0 , r1 , c1 , x, and y) are rounded to the nearest integer if they aren™t integers
already. Furthermore r0 is replaced by max(0, min(maxr , r0 )) and r1 is replaced by
max(r0 , min(maxr , r1 )), where maxr is the maximum number of rows on the screen;
similar adjustments are made to c0 and c1 . The two (r, c) values are row and column
numbers on the screen; the topmost row is conventionally taken to be row zero, and the
leftmost column is taken to be column zero. (These conventions for screen coordinates
are quite di¬erent from the normal Cartesian coordinate system used everywhere else in
, but somehow they seem appropriate when applied to screens.) Point (x, y)
of ™s raster will be equated to the upper left corner of the rectangle, i.e., to
the upper left corner of the pixel in screen column c0 of screen row r0 . The window
itself occupies r1 ’ r0 rows and c1 ’ c0 columns. It follows that the pixel in column c1
of row r1 is not in the window itself, but it is the screen pixel diagonally just below
and to the right of the lower right corner of the window.
What are the coordinates of the boundary of such a window?
192 Chapter 23: Online Displays

If you run on a system that doesn™t support general bitmap dis- nullpicture
plays, the display and openwindow commands will do nothing. You™ll have
to look at hardcopy output, o¬line. (But your might run a bit faster.) meta-characters
The syntax for display insists that you display a picture variable , not a a
picture expression ; thus, you can™t ˜display nullpicture™. Plain -
de¬nes a special variable blankpicture that™s entirely blank, just so that you can
easily display nothing whenever you like.

A window may be opened any number of times, hence moved to di¬erent
locations on the screen. Opening a window blanks the corresponding screen
rectangle as if you had displayed blankpicture .

The e¬ect of overlapping windows is unde¬ned, because does not
always repaint pixels that have remained unchanged between displays.

Changes to a picture do not change the displays that were generated from it,
until you give another display command explicitly. Thus, the images embla-
zoned on your screen might not exist any longer in ™s picture memory.

has an ˜openit™ macro that opens currentwindow ; this vari-
able currentwindow is always zero unless you change it yourself. The showit
macro displays currentpicture in currentwindow ; and it™s also designed to call openit”
but only the very ¬rst time showit is invoked. This means that the screen normally
won™t be touched until the moment you ¬rst try to display something.

Appendix E explains how to manage a more elaborate scheme in which six
windows can be used to show how meta-characters vary under six di¬erent
font-parameter settings. The author used such a six-window system when developing
the Computer Modern typefaces; here is a typical example of what appeared on his
terminal when the letter ˜a™ was being re¬ned:

(Figure 23 will be inserted here; too bad you can™t see it now.)
Chapter 23: Online Displays 193

EXERCISE 23.2 new window
screen cols
The openit macro in Appendix B speci¬es (’50, 300) as the upper left corner
point of the window used for showing all the pictures. This might clip o¬ the bottom STRAVINSKY
of a large character, if your screen is limited to, say, 360 rows. How could you change
openit so that the character images will be raised 20 rows higher than they would be
in the standard setting?
Design a ˜new window™ routine that allocates windows 1, 2, . . . , 15. If the
user says ˜new_window $(u,v)™, where $ is any su¬x and u,v are pairs of coordinates
for two opposite corners of a rectangle, your macro should map that rectangle to the
next available screen rectangle and open it as window number window$. The allocation
should be left to right, top to bottom; assume that the screen is an in¬nite rectangle,
screen cols wide.

Editing will be done on-line with a display scope and keyboard.
” RICHARD L. VENEZKY, in American Documentation (1968)

In future I might be obliged to turn for material to the tube.
” IGOR STRAVINSKY, in Harper™s (1970)
(page 194)

and Discretion
Chapter 24: Discreteness and Discretion 195

Pixel patterns are indistinguishable from continuous curves, when the pixels are luxo
small enough. After all, the human eye is composed of discrete receptors, and hand-tuning
visible light has a ¬nite wavelength. Our hypothetical luxo printer of Chapter 11, raster
with its resolution of 2000 pixels per inch, would surely be able to produce printed smoothing
pages of high quality, if it existed; the physical properties of ink would smooth digitizing
out all the tiny bumps, obliterating all the evidence that the letterforms had
been digitized. However, it will always be less expensive to work with devices
of lower resolution, and we want the output of to look as good as
possible on the machines that we can a¬ord to buy. The purpose of this chapter
is to discuss the principles of “discreet rounding,” i.e., to consider the tasteful
application of mathematical techniques by which can be made to
produce satisfactory shapes even when the resolution is rather coarse.
The technical material in this chapter is entirely marked with danger
signs, since careful rounding tends to make programs more complex;
a novice user will not wish to worry about such details. On the other hand,
an expert er will take pains to round things properly even when
preparing high-resolution fonts, since the subtle re¬nements we are about to
discuss will often lead to signi¬cantly better letterforms.
We should realize before we begin that it would be a mistake to set our
hopes too high. Mechanically generated letters that are untouched by human
hands and unseen by human eyes can never be expected to compete with al-
phabets that are carefully crafted to look best on a particular device. There™s
no substitute for actually looking at the letters and changing their pixels until
the result looks right. Therefore our goal should not be to make hand-tuning
obsolete; it should rather be to make hand-tuning tolerable. Let us try to create
meta-designs so that we would never want to change more than a few pixels
per character, say half a dozen, regardless of the resolution. At low resolutions,
six pixels will of course be a signi¬cant percentage of the whole, and at higher
resolutions six well-considered pixel changes can still lead to worthwhile improve-
ments. The point is that if our design comes close enough, a person with a good
bitmap-editing program will be able to optimize an entire font in less than an
hour. This is an attainable goal, if rounding is done judiciously.
tries to adjust curves automatically, so that they are well adapted
to the raster, if the internal quantities autorounding and/or smoothing have
sets autorounding := 2 and smoothing := 1, so you
positive values. (Plain
generally get these features unless you turn them o¬ yourself.) But all the examples in
this chapter will be generated with autorounding := smoothing := 0 unless otherwise
mentioned, because this will keep ™s automatic mechanisms from interfering
with our experiments. We shall discuss the pros and cons of automatic rounding after
we have explored the general problem in more detail.
The ¬rst thing we need to understand about rounding is ™s pro-
cedure for digitizing a path. A path of length n can be regarded as a trajec-
tory z(t) that is traced out as t varies from 0 to n. In these terms, the corresponding
digitized path is most easily described by the formula ˜round z(t)™ for 0 ¤ t ¤ n; each
196 Chapter 24: Discreteness and Discretion

z(t) is rounded to the nearest point with integer coordinates. For example, if a path lowres
goes through point (3.1, 5.7), its digitization will go through point (3, 6). The digitized
trajectory makes discrete jumps at certain values of t, when round z(t) hops from one
point to another; the two points will be one pixel apart, and we can imagine that the
digitized path traverses the horizontal or vertical edge between them when it jumps.
When an ordinary region is being ¬lled, this rule for digitizing paths boils
down to a simple criterion that™s easy to visualize: A pixel belongs to the
digitized region if and only if its center point lies inside the original undigitized path.
For example, two versions of Chapter 5™s Ionian ˜ ™ are shown here at a resolution of
200 pixels per inch, using the characteristics of lowres mode in Appendix B:

(Figure 24a&b will be inserted here; too bad you can™t see it now.)

The heavy broken lines are digitized paths, and the pixels inside these ragged bound-
aries are those whose centers lie in the shaded regions.
The ˜ ™ on the left has digitized well; but the one on the right has problems,
because it was based on curves that were generated without taking the raster
into account. The di¬erence between these two letters is entirely due to line 8 of the
program in Chapter 5, which says
curve sidebar = round 1/18em ;
this equation determines the position of the leftmost and rightmost edges of the ˜ ™
before digitization, and it leads to the nice digitized form in the left-hand example.
Without the word ˜round™, we get the inferior right-hand example, which was obtained
program except that curve sidebar was set to 1/18em
by exactly the same
exactly. One little token”which changed an exact calculation to an approximate,
rounded calculation”made all the di¬erence!
Curves that are placed in arbitrary positions on a raster can lead to digital
disasters, even though the curves themselves aren™t bad. For example, suppose
we take the right-hand example above and shift it just 0.05 and 0.10 pixels to the right:

(Figure 24c&d will be inserted here; too bad you can™t see it now.)
Chapter 24: Discreteness and Discretion 197

The ¬rst shift of 0.05 pixels causes a tiny pimple to appear at the right edge; after an- pimple
other small shift the pimple has grown into a mole, and the left edge has become too ¬‚at.
de¬ne corrected pixels
A designer who is asked to make a digital ˜O™ that is 22 pixels wide will beginchar
certainly have pixels in mind when making the design. Therefore it™s not
surprising that our program to generate a digital ˜O™ should pay attention to actual
pixel positions by rounding curve sidebar as in this example. We have distorted the
in¬nite-resolution curve slightly so that it will digitize well, before digitizing it.
A path z(t) will digitize well if the digitization process doesn™t change it too
much; thus, we want z(t) to be essentially the same as round z(t), at all the
important places. But what places are “important”? Experience shows that the most
critical points are those where the path travels horizontally or vertically, i.e., where it
runs parallel to the raster lines. It™s best to arrange things so that a curve becomes
parallel to the raster lines just when it touches or nearly touches those lines; then it
will appear to have the right curvature after digitization. The worst case occurs when
a curve becomes parallel to the raster just when it™s halfway between raster lines; then
it gets a pimple or a ¬‚at spot.
Diagonal slopes, where a curve has a ±45—¦ tangent angle, are also potential
sources of unwanted pimples and ¬‚ats. Similarly, at higher resolutions it is
sometimes possible to detect small glitches when a curve travels with slopes of ±1/2 or
±2/1. Rational slopes m/n where m and n are small integers turn out to be somewhat
dangerous. But diagonals are of secondary importance; horizontal and vertical slopes
lead to more severe problems.
These considerations suggest a simple general principle for adapting the out-
lines of shapes to be digitized: If you know that the outline will have a vertical
tangent at some point, round the x coordinate to an integer and leave the y coordinate
unchanged. If you know that the outline will have a horizontal tangent at some point,
round the y coordinate to an integer and leave the x coordinate unchanged.
Incidentally, the horizontal tangent points in our ˜ ™ examples were taken
care of by the fact that ˜de¬ne corrected pixels™ makes the overshoot pa-
rameter o nearly an integer, together with the fact that beginchar makes h an integer.
If the y coordinates had not been rounded at the horizontal tangent points, our bad
examples would have looked even worse.
Before we go further into the study of rounding, we had better face up to a
technicality that™s sometimes important: We said that the pixels of a digi-
tized region are those whose centers lie inside the undigitized region; but this rule is
vague about what happens when the centers happen to fall precisely on the undigitized
boundary. Similarly, when we said that round z(t) jumps from one point to an adjacent
point, we ignored the fact that a curve such as z(t) = (t, t) actually jumps from (0, 0)
to (1, 1) when it is rounded as t passes 1/2; those points are not adjacent.
skirts both of these problems in an interesting way: It shifts all of its paths to the right
by an in¬nitesimal amount δ, and it also shifts them upward by an even smaller in-
¬nitesimal amount δ , so that no path actually touches a pixel center. Here δ and are
positive numbers that are chosen to be so small that their actual values don™t matter.
For example, the path z(t) = (t, t) becomes (t + δ, t + δ ), which jumps from (0, 0) to
(1, 0) to (1, 1) because it momentarily rounds to (1, 0) when t = 1/2 ’ 2δ .
198 Chapter 24: Discreteness and Discretion

Points of the form (m + 1/2, n + 1/2), where m and n are integers, lie in the ambiguous point
centers of their pixels. They are called “ambiguous” points because we can™t
round them to the nearest integer neighbor without deciding which of four adjacent good.x
points is to be considered the nearest. If we imagine taking a curved outline and shifting good.y
it slowly to the right, the digitized image makes abrupt transitions when the outline
passes over an ambiguous point. When a path comes near an ambiguous point, the
path is farthest away from its digitization. Thus the ambiguous points are points of
instability, and digitizing works best when paths don™t get too close to them.
Let™s consider now what happens when we draw with a pen, instead of ¬lling
an outline. It may seem that the simplest possible draw command would be
something like this:
pickup pencircle; draw (0, 0) . . (10, 0);
what could be easier? But a closer look shows that this is actually about the worst case
that could be imagined! A circular pen of diameter 1 that goes from (0, 0) to (10, 0) has
upper and lower boundaries that go from (0, ±1/2) to (10, ±1/2), and both of these
boundaries run smack through lots of ambiguous points. has to decide
whether to ¬ll the row of pixels with 0 ¤ y ¤ 1 or the lower row with ’1 ¤ y ¤ 0,
neither of which is centered on the given line. According to the rule stated earlier,
shifts the path very slightly to the right and very, very slightly up; thus
the pixels actually ¬lled are bounded by (0, 0) - - (10, 0) - - (10, 1) - - (0, 1) - - cycle.
Continuing this example, what pixels would have been ¬lled if the path had
been ˜(0, 0) . . (10, ’epsilon )™ ?
In general when we draw with a ¬xed pen, good digitizations depend on
where the edges of the pen happen to fall, not on the path followed by the
pen™s center. Thus, for example, if the path we™re drawing has a vertical tangent at
point z1 , we don™t necessarily want x1 to be an integer; we want lft x1 and rt x1 to
be integers. If there™s a horizontal tangent at z2 , we want top y2 and bot y2 to be
integers. The pens created by pencircle always have the property that (lft x) ’ (rt x)
and (top y) ’ (bot y) are integers; hence both edges will be in good or bad positions
Suppose that we want x1 to be approximately equal to ±, and we also want
it to be at a good place for vertical tangents with respect to the pen that has
currently been picked up. One way to de¬ne x1 is to say
lft x1 = round(lft ±);
this does the right thing, because it makes lft x1 an integer and it also makes x1 ≈ ±.
Similarly, to make y2 ≈ β good for horizontal tangents, we can say
top y2 = round(top β).
Such operations occur frequently in practice, so plain provides convenient
abbreviations: We can say simply
x1 = good.x ±; y2 = good.y β
instead of using indirect equations for lft x1 and top y2 .
Chapter 24: Discreteness and Discretion 199

Let™s look one last time at the letters of the logo, in order to make logo.mf
them round properly. Chapter 11 describes a ¬le logo.mf that draws the seven
characters, but we can improve the results by making pixel-oriented re¬nements. In leftstemloc
the ¬rst place, we can replace the command barheight
de¬ne pixels
de¬ne whole pixels
de¬ne pixels(s, u, xgap , ygap , leftstemloc , barheight ) de¬ne good x pixels
de¬ne good y pixels
by something better: Looking at the uses of these ad hoc dimensions, we see that xgap O
and ygap ought to be integers; leftstemloc should be a good.x value for logo pen ; and
barheight should be a good.y value. Therefore we say change width

de¬ne pixels(s, u);
whole pixels(xgap , ygap );
de¬ne good x pixels(leftstemloc );
de¬ne good y pixels(barheight );

, will do the right thing. (The logo pen
these commands, provided by plain
should be picked up before the last two commands are given.) These few changes, and
a change to the ˜ ™, su¬ce to ¬x all the letters except ˜ ™.

The program for ™s ˜ ™ appears in Chapter 18. What changes
would you suggest to make it digitize well?

The ˜ ™ presents a new problem, because we want it to be symmetric between
left and right. If the pen breadth is odd, we want the character width w to
be odd, so that there will be as many pixels to the left of the stem as there are to the
right. If the pen breadth is even, we want w to be even. Therefore we have a 50-50
chance of being unhappy with the value of w that is computed by beginchar.

Prove that the value of w is satisfactory for ˜ ™ with respect to the logo pen
if and only if .5w is a good x value for vertical strokes.

If w is not a good value, we want to replace it by either w+1 or w’1, whichever
is closer to the device-independent width from which w was rounded. For
example, if w was rounded to 22 from the ideal width 21.7, we want to change it to 21
rather than 23. Plain ™s change width routine does this. Hence we have
the following program for ˜ ™, in place of the simpler version found in exercise 11.4:

beginlogochar("T", 13);
if .5w <> good.x .5w: change width; ¬
lft x1 = ’eps ;
x2 = w ’ x1 ;
x3 = x4 = .5w;
y1 = y2 = y3 ; top y1 = h; bot y4 = ’o; (Figure 4b will be inserted here;
too bad you can™t see it now.)
draw z1 - - z2 ; draw z3 - - z4 ;
labels(1, 2, 3, 4); endchar.

Chapter 4 said that ˜ ™ was the simplest of the seven
logo letters, but it has turned out to be the trickiest.
200 Chapter 24: Discreteness and Discretion

This program has one unexplained feature. Why was lft x1 set to ’eps instead eps
of zero? The answer requires an understanding of the pen polygons discussed
in Chapter 16. The edges of those polygons are highly likely to pass through ambiguous pencircle
points when the center of the pen has integer or half-integer coordinates. draw
sharp sign
shifts paths slightly to the right and up, in order to resolve ambiguities; therefore if hash mark
ambiguous points occur at the left and right edges of the ˜ ™, some pixels will be lost range
at the left but gained at the right. The constant eps is 0.00049, which is small but
will surely notice it. Subtracting eps from x1 and
positive enough that
adding eps to x2 avoids ambiguous edge points and keeps the result symmetric.

Since the overshoot ˜o™ is always eps more than an integer, it is unnecessary
to do anything similar at point z4 ; the equation ˜bot y4 = ’o™ is su¬cient.

Point z3 in the middle of the ˜ ™ is in a satisfactory position because bot y3 =
ygap ’ o . If bot y3 were exactly an integer, the would often turn out to be
unsymmetric, because of ambiguous points on the boundary at z3 .

True or false: If currentpen is pencircle xscaled px yscaled py , the command
˜draw (’epsilon , 0) . . (+epsilon , 0)™ will produce an image that has both left-right
and top-bottom symmetry. (Assume that autorounding =smoothing =0.)

The polygon for ˜pencircle scaled 3™ is an octagon whose vertices are at the
points (±0.5, ±1.5) and (±1.5, ±0.5). Prove that if you ˜draw (x, y)™ with this pen,
the result never has both top-bottom and left-right symmetry.

Rounding can also help to position points at which we don™t have horizontal
or vertical tangents. For example, consider the “sharp sign” or “hash mark”
character that™s drawn by the following program:

u# := 10 pt #; de¬ne pixels(u);
beginchar (0, 15u#, 250 pt #, 70 pt #);
36 36
pickup pencircle
scaled (.4pt + blacker );
lft x1 = round u ’ eps ;
x3 = x1 ;
x2 = x4 = w ’ x1 ;
y1 = y2 = good.y (.5[’d, h] + pt );
y3 = y4 = h ’ d ’ y1 ;
draw z1 - - z2 ; draw z3 - - z4 ;
lft x6 = round 3u;
x7 = w ’ x6 ; (Figure 24e will be inserted here; too bad you
can™t see it now.)
x8 = good.x .5w;
x5 ’ x6 = x7 ’ x8 ;
top y5 = top y7 = h + eps ;
bot y6 = bot y8 = ’d ’ eps ;
draw z5 - - z6 ; draw z7 - - z8 ;
labels(range 1 thru 8);
Chapter 24: Discreteness and Discretion 201

If we digitize this character according to lowres mode at 200 pixels per inch, we get lowres
the following results:

The left-hand example was obtained by omitting the ˜round™ and ˜good.x ™ instructions
in the equations for x6 and x8 . This meant that points z6 and z8 fell into di¬erent,
possibly unlucky, raster positions, so the two diagonal strokes digitized di¬erently even
though they came from essentially identical undigitized lines. The middle example
was produced by the given program without changes. And the right-hand example
was produced by drawing the diagonals in a more complicated way: The commands
˜draw z5 - - z6 ; draw z7 - - z8 ;™ were replaced by
y15 = y1 ; z15 = whatever [z5 , z6 ]; y36 = y3 ; z36 = whatever [z5 , z6 ];
y27 = y2 ; z27 = whatever [z7 , z8 ]; y48 = y4 ; z48 = whatever [z7 , z8 ];
draw z5 - - (good.x (x15 + .5), y1 ) - - (good.x (x15 ’ .5), y1 )
- - (good.x (x36 + .5), y3 ) - - (good.x (x36 ’ .5), y3 ) - - z6 ;
draw z7 - - (good.x (x27 + .5), y2 ) - - (good.x (x27 ’ .5), y2 )
- - (good.x (x48 + .5), y4 ) - - (good.x (x48 ’ .5), y4 ) - - z8 ;
The idea here was to control the goodness of the points where the diagonals intersect
the horizontal bar lines, and to hide one of the “jaggies” inside each bar line. If we
do the same three experiments but triple the resolution, we get similar results but the
di¬erences are not quite so obvious:

When letters are drawn by ¬lling outlines, the left and right outlines are
digitized independently; therefore corresponding outlines should usually be
o¬set from each other by an integer amount whenever possible. For example, suppose
that the letter ˜n™ is being drawn with commands like
penpos2 (stem , 0); penpos4 (stem , 0)
to specify the stroke widths at the base of the two stems. We will therefore have
x2r ’ x2l = x4r ’ x4l = stem . If stem is not an integer, say stem = 2.7, we might have
x2l = 2.1, x2r = 4.8, x4l = 9.6, x4r = 12.3; then x2r ’ x2l will digitize to 5 ’ 2 = 3,
so the left stem will be three pixels wide, but the right stem will be only 12 ’ 10 = 2
pixels wide. We could get around this problem by insisting that either x2l or x2r be an
202 Chapter 24: Discreteness and Discretion

integer, and that either x4l or x4r be an integer; then both stems would be three pixels de¬ne whole blacker pixels
wide. But other quantities calculated from stem (e.g., the breadth of diagonal strokes)
would then be based on a value of 2.7 instead of the stem width 3 that an observer of
the font actually perceives. Therefore it is best to make stem an integer. The proper
way to do this is generally to say
de¬ne whole blacker pixels(stem );
this command computes stem from stem # by the formula
stem := max(1, round(stem # — hppp + blacker )).
(Notice that this rounding operation is not allowed to reduce stem to zero at low
Even when the stem width is an integer in the ˜n™ example, we probably want
to arrange things so that x2l , x2r , x4l , and x4r are integers, because this will
give the least distortion under digitization. Suppose, however, that it™s most convenient
to de¬ne the pen position at the center of the stroke instead of at the edge; i.e., the
program would say just ˜x2 = ±™ if rounding were not taken into account. How should
x2 be de¬ned, when we want x2l to be an integer? We could say
x2 = ±; x2l := round x2l ; x2r := round x2r ; x2 := .5[x2l , x2r ]
but that™s too complicated; moreover, it will fail if any other variables depend on x2 ,
x2l , or x2r , because such dependencies are forgotten when new values are assigned. In
the case of ¬xed pens we solved this problem by saying ˜x2 = good.x ±™; but the good.x
function doesn™t know about stem . One solution is to say
x2l = round(± ’ .5stem ),
or equivalently, ˜x2r = round(± + .5stem )™. This does the job all right, but it isn™t com-
pletely satisfying. It requires knowledge of the breadth that was speci¬ed in the penpos2
command, and it works only when the penpos angle is 0. If the penpos command is
changed, the corresponding equation for rounding must be changed too. There™s an-
other solution that™s more general and more attractive once you get used to it:
x2l = round(x2l ’ (x2 ’ ±)).
Why does this work? The argument to ˜round™ must be a known value, but both
x2l and x2 are unknown. Fortunately, their di¬erence x2l ’ x2 is known, because of
the penpos2 command. The rounding operation makes x2 ≈ ± because it makes x2l
approximately equal to the value of x2l minus the di¬erence between x2 and ±.
The generality of this technique can be appreciated by considering the follow-
ing more di¬cult problem that the author faced while designing a ˜w™: Suppose you
want x1 ’ x2 to be an integer and x3 ≈ x4 , and suppose that x2 , x3 ’ x1 , and x4 + x1
are known; but x1 is unknown, hence x3 and x4 are also unknown. According to our
general idea, we want to specify an equation of the form ˜x1 ’ x2 = round(x1 ’ x2 + f )™,
where x1 ’ x2 + f is known and f is a formula that should be approximately zero. In
this case x3 ’ x4 is approximately zero, and (x3 ’ x1 ) ’ (x4 + x1 ) is known; what value
of f should we choose?
Chapter 24: Discreteness and Discretion 203

In many fonts, such as the one you are now reading, curved lines swell out o
Computer Modern
so that the thick parts of ˜o™ are actually a bit broader than the stems of ˜n™.
Therefore the Computer Modern font routines discussed in Appendix E have two pa- font setup
rameters, stem # and curve #, to govern the stroke thickness. For example, the font stem
cmr9 used in the present paragraph has stem # = 2/3pt # and curve # = 7/9pt #. Both lowres ¬x
of these should be integers, hence the font setup macro in Appendix E dutifully says triangle

de¬ne whole blacker pixels(stem , curve ).

Although this looks good on paper, it can cause problems at certain low resolutions,
because the rounding operation might make stem and curve rather di¬erent from each
other even though stem # and curve # are fairly close. For example, the resolution might
be just at the value where cmr9™s stem turns out to be only 2 but curve is 3. Curves
shouldn™t be that much darker than stems; they would look too splotchy. Therefore
plain has a ˜lowres ¬x™ subroutine, and Appendix E says

lowres ¬x(stem ,curve ) 1.2

after stem and curve have been de¬ned as above. In this particular case lowres ¬x will
reset curve := stem if it turns out that the ratio curve /stem is greater than 1.2 times
the ratio curve #/stem #. Since curve #/stem # = 7/6 in the case of cmr9, this means
that the ratio curve /stem after rounding is allowed to be at most 1.4; if curve = 3 and
stem = 2, the curve parameter will be lowered to 2. In general the command

lowres ¬x(d1 , d2 , . . . , dn ) r

will set dn := · · · d2 := d1 if max(d1 , d2 , . . . , dn )/ min(d1 , d2 , . . . , dn ) is greater than
r · max(d1 #, d2 #, . . . , dn #)/ min(d1 #, d2 #, . . . , dn #).

Good digitization can also require attention to the
shapes of the digitized angles where straight lines meet.
The purpose of the present exercise is to illustrate the rel-
evant ideas by studying the ˜ ™ symbol, for which a pro- (Figure 4e will be inserted
gram appears in Chapter 4. If that program is used with- here; too bad you can™t see
it now.)
out change to produce low-resolution triangles, the results
might turn out to be unsatisfactory because, for example,
the point of the triangle at the right might digitize into a
snubnosed or asymmetric shape. If y3 is an integer, the
triangle will be top-bottom symmetric, but the right-hand
tip will be two pixels tall and this will look too blunt. Therefore we should choose y3 to
be an integer plus 1/2. Given this value of y3 , what will be the shape of the rightmost
four columns of the digitized tip, as x3 varies?

Continuing the previous exercise, assume that x1 is an integer. What value
of y1 will make the upper tip of the triangle look like ˜ ™ after digitization?

Concluding the previous exercise, modify the program of Chapter 4 so that
the upper tip and the upper part of the right tip both digitize to the shape ˜ ™.
204 Chapter 24: Discreteness and Discretion

So far in this chapter we™ve assumed that pixels are square. But sometimes we nonsquare
need to prepare output for devices with general rectangular pixels, and this
aspect ratio
adds an extra dimension of complexity to rounding. Plain sets things up top
so that currenttransform multiplies all y coordinates by aspect ratio , when paths are bot
¬lled or drawn, or when pens are picked up. Furthermore the top and bot functions overshoot
divide the amount of o¬set by aspect ratio . This means that programs can beginchar
still be written as if pixels were square; the normal ˜angle™ and ˜direction™ functions,
etc., can be used. But the good places for rounding horizontal tangents are not at good.y
integer values of y in general, they are actually at values that will become integers logo
de¬ne whole vertical pixels
after multiplication by the aspect ratio.
de¬ne horizontal corrected pixels
The vround function rounds its argument to the nearest y coordinate that
corresponds to a pixel boundary in the general case. Thus if aspect ratio = 1, F
vround simply rounds to the nearest integer, just like ˜round™; but if, say, aspect ratio =
4/3, then vround will round to the nearest multiple of 3/4. Plain uses good.lft
vround instead of ˜round™ when it computes an overshoot correction, and also when good.rt
beginchar computes the values of h and d . The good.y function produces a good
y value that takes aspect ratio properly into account. autorounding
Without looking at Appendix B, try to guess how the vround and good.y
macros are de¬ned.
What are the “ambiguous points” when pixels are not square?
The logo as we have described it so far will round properly with
respect to arbitrary aspect ratios if we make only a few more re¬nements. The
value of ygap should be vrounded instead of rounded, so we initialize it by saying
de¬ne whole vertical pixels(ygap ).
Furthermore we should say
ho # := o #; de¬ne horizontal corrected pixels(ho );
and ho should replace o in the equations for x4 in the programs for ˜ ™ and ˜ ™.
Everything else should work satisfactorily as it stands.
Appendix B includes macros good.top , good.bot , good.lft , and good.rt that take
pairs as arguments. If you say, for example, ˜z3 = good.top (±, β)™ it means
that z3 will be near (±, β) and that when z3 is modi¬ed by currenttransform the top
point of currentpen placed at the transformed point will be in a good raster position.
™s ˜autorounding ™ feature tries to adjust curves to the raster for
you, but it is a mixed blessing. Here™s how it works: If the internal quantity
autorounding is positive, the x coordinates of all paths that are ¬lled or drawn are
rounded to good raster positions wherever there™s a vertical tangent; and the y coordi-
nates are rounded to good raster positions wherever there™s a horizontal tangent. The
rest of the curve is distorted appropriately, as if the raster were stretching or shrinking
slightly. If autorounding > 1, you get even more changes: paths are perturbed slightly
at ±45—¦ tangent directions, so that second-order pimples and ¬‚at spots don™t appear
Chapter 24: Discreteness and Discretion 205

For example, if we return to the Ionian ˜ ™ with which we began this chapter, granularity
let™s suppose that curve sidebar was left unrounded. We saw that the result
was bad when autorounding was 0; when autorounding = 1 and 2 we get this:

(Figure 24f&g will be inserted here; too bad you can™t see it now.)

The stroke has gotten a lot thinner at the sides, by comparison with the original design
(which, incidentally, can be seen in the illustrations below). Although autorounding
has produced a fairly recognizable O shape, the character of the original has been lost,
especially in the case autorounding = 2; indeed, the inner outline has been brought
towards the center, in the upper left and lower right sectors, and this has made the
digitized inner boundary perfectly symmetric!
There™s an internal quantity called granularity , normally equal to 1, which
a¬ects autorounding by e¬ectively scaling up the raster size. If, for example,
granularity = 4, the autorounded x coordinates and y coordinates will become multi-
ples of 4 instead of simply integers. The illustrations above were produced by setting
granularity = 10 and mag = 10; this made the e¬ects of autorounding visible. The
granularity should always be an integer.
Besides autorounding , there™s a ˜smoothing™ feature that becomes active when
smoothing > 0. The basic idea is to try to make the edges of a curve fol-
low a regular progression instead of wobbling. A complete discussion of the smooth-
ing algorithm is beyond the scope of this manual, but an example should make the
general idea clear: Let™s use the letters R and D to stand for single-pixel steps to
the right and down, respectively. If a digitized path goes ˜RDDRDRDDD ™, say, the
number of downward steps per rightward step is ¬rst decreasing, then increasing; the
smoothing process changes this to ˜RDDRDDRDD ™. If smoothing is applied to the
Ionian ˜ ™ shapes above, nothing happens; but if we go back to the original obtained
with autorounding = 0, we get a few changes:

(Figure 24b&h will be inserted here; too bad you can™t see it now.)

Three pixels have been added by smoothing in the right-hand illustration; e.g., a pattern
206 Chapter 24: Discreteness and Discretion

If you do your own rounding, it turns out that autorounding and smoothing slant
usually change very few pixels, if any; thus your safest strategy is probably to
Computer Modern
turn them o¬ in such cases. If you de¬ne your strokes by outlines, autorounding and italic
smoothing apply independently to the left and right edges, so they may hurt as often as tracingspecs
they help; again, they should probably be turned o¬. But if you are drawing with ¬xed ENE
pens, autorounding generally works well and saves a lot of fuss. If the pens are circles ESE
or nearly circles, smoothing is also helpful; but if the pens are more “calligraphic,”
compass directions
they are supposed to produce nonsmooth edges occasionally, so you had better set
smoothing := 0.

If you “slant” a font by modifying currenttransform as described in Chap-
ter 15, positions of horizontal tangency will remain the same. But positions
of vertical tangency will change drastically, and they will probably not fall in known
parts of your design. This means, for example, that autorounding will be helpful in
a slanted pen-generated font like the ˜ ™ logo. However, the author found
that the outline-generated letters of Computer Modern italic came out better with
autorounding = 0, because autorounding tended to make some characters too dark
and others too light.

The e¬ect of autorounding can be studied numerically if you set tracingspecs
to a positive value; this displays ™s internal calculations as it ¬nds
horizontal, vertical, and diagonal tangent points. ( prepares to digitize
paths by ¬rst subdividing each B´zier segment into pieces that travel in only one
“octant” direction.) For example, if autorounding = 0 and tracingspecs = 1, and if
curve sidebar is left unrounded, the ¬le io.log will contain the following information
about the outer curve of the ˜ ™:

Path at line 15, before subdivision into octants:
(1.53745,9.05345)..controls (1.53745,4.00511) and (5.75409,-0.00049)
..(10.85147,-0.00049)..controls (16.2217,-0.00049) and (20.46255,4.51297)
..(20.46255,9.94655)..controls (20.46255,14.99713) and (16.23842,19.00049)
..(11.13652,19.00049)..controls (5.77066,19.00049) and (1.53745,14.48491)
Cycle spec at line 15, after subdivision:
(1.53745,9.05345) % beginning in octant ˜SSE™
..controls (1.53745,6.58786) and (2.54324,4.371)
..(4.16621,2.74803) % segment 0
% entering octant ˜ESE™
..controls (5.8663,1.04794) and (8.24362,-0.00049)
..(10.85147,-0.00049) % segment 0
% entering octant ˜ENE™

. . . and so on; there are lots more numbers! What does this all mean? Well, the
¬rst segment of the curve, from (1.53745, 9.05345) to (10.85147, ’0.00049), has been
subdivided into two parts at the place where the slope is ’1. The ¬rst of these parts
travels basically ˜South by South East™ and the second travels ˜East by South East™. The
other three segments are subdivided in a similar way (not shown here). If you try the
same experiment but with autorounding = 1, some rather di¬erent numbers emerge:
Chapter 24: Discreteness and Discretion 207

Cycle spec at line 15, after subdivision and autorounding: CARTER
(2,9.05348) % beginning in octant ˜SSE™
..controls (2,6.50526) and (3.02194,4.22272)
..(4.6577,2.58696) % segment 0
% entering octant ˜ESE™
..controls (6.2624,0.98225) and (8.45786,0)
..(10.85873,0) % segment 0
% entering octant ˜ENE™
Point (1.53745, 9.05345), where there was a vertical tangent, has been rounded to
(2, 9.05348); point (10.85147, ’.00049), where there was a horizontal tangent, has been
rounded to (10.85873, 0); the intermediate control points have been adjusted accord-
ingly. (Rounding of x coordinates has been done separately from y coordinates.)
Finally, with autorounding = 2, additional adjustments are made so that the 45—¦ tran-
sition point will occur at what thinks is a good spot:
Cycle spec at line 15, after subdivision and double autorounding:
(2,9.05348) % beginning in octant ˜SSE™
..controls (2,6.6761) and (3.07103,4.42897)
..(4.78537,2.71463) % segment 0
% entering octant ˜ESE™
..controls (6.46927,1.03073) and (8.62749,0)
..(10.85873,0) % segment 0
% entering octant ˜ENE™
(Notice that 4.78537 + 2.71463 = 7.50000; when the slope is ’1 at a transition point
(x, y), the curve stays as far away as possible from ambiguous points near the transition
if x + y + .5 is an integer.)

” PIERRE LE BE, B´le Pr´rie (1601)
e e

” MATTHEW CARTER, Bell Centennial (1978)
(page 208)

Summary of
Chapter 25: Summary of Expressions 209

We™ve seen that can handle a wide variety of algebraic expressions; expressions
now it™s time to consolidate what we have learned. The purpose of this chapter hierarchy
and the one that follows is to present a precise and concise summary of everything vacuous
that knows how to do. secondary
We shall be concerned here solely with ™s primitive opera- tertiary
tions, rather than with the higher-level features of the plain base
that comprise the bulk of typical programs. Therefore novice users should put
o¬ reading Chapters 25 and 26 until they feel a need to know what goes on at
the more mundane levels inside the computer. Appendix B contains a summary
of the features of plain , together with a ready-reference guide to the
things that most people want to know about usage.
The remainder of this chapter is set in small type, like that of the present
paragraph, since it is analogous to material that is marked “doubly dangerous” in
other chapters. Instead of using dangerous bend signs repeatedly, let us simply agree
that Chapters 25 and 26 are dangerous by de¬nition.
Chapter 8 introduced the general idea of expressions and the four-fold “pri-
mary, secondary, tertiary, expression” hierarchy on which their syntax is based. -
™s variables can have any of eight types: boolean, numeric, pair, path, pen,
picture, string, and transform. Its expressions can actually have nine di¬erent
types, although the ninth one”“vacuous””is not particularly interesting since it has
only one possible value. Here is the overall syntax:

primary ’’ boolean primary | numeric primary
| pair primary | path primary
| pen primary | future pen primary
| picture primary | string primary
| transform primary | vacuous primary

secondary ’’ boolean secondary | numeric secondary
| pair secondary | path secondary
| pen secondary | future pen secondary
| picture secondary | string secondary
| transform secondary | vacuous secondary

tertiary ’’ boolean tertiary | numeric tertiary
| pair tertiary | path tertiary
| pen tertiary | picture tertiary
| string tertiary | transform tertiary
| vacuous tertiary

expression ’’ boolean expression | numeric expression
| pair expression | path expression
| pen expression | picture expression
| string expression | transform expression
| vacuous expression

We shall discuss the di¬erent types of expressions in alphabetic order; thus, if you are
dying to know what a “vacuous” expression is, you should skip to the end of the chapter.
210 Chapter 25: Summary of Expressions

Boolean expressions were discussed in Chapter 19. The full syntax has one boolean primary
more operation, ˜charexists™, that was not mentioned there:
boolean primary ’’ boolean variable | boolean argument )
| true | false begingroup
| ( boolean expression ) endgroup
| begingroup statement list boolean expression endgroup unknown
| known primary | unknown primary cycle
| type primary | cycle primary odd
| odd numeric primary not
| charexists numeric primary boolean secondary
| not boolean primary boolean tertiary
boolean secondary ’’ boolean primary or
| boolean secondary and boolean primary boolean expression
boolean tertiary ’’ boolean secondary ¡
| boolean tertiary or boolean secondary ¡=
boolean expression ’’ boolean tertiary ¿
| numeric expression relation numeric tertiary =
| pair expression relation pair tertiary ¡¿
| transform expression relation transform tertiary charcode
| boolean expression relation boolean tertiary tokens
| string expression relation string tertiary parentheses
relation ’’ < | <= | > | >= | = | <> delimiters
The expression ˜charexists x™ is true if and only if a shipout command has previously expr
been done with charcode = x. (The value of x is ¬rst rounded to an integer, and argument
reduced to the range 0 ¤ x < 256 by adding or subtracting multiples of 256.) capsule
numeric atom
In these rules, tokens like ˜true™ that appear in typewriter type stand for any normaldeviate
tokens whose current meaning is the same as the meaning of ˜true™ when
starts from scratch; the particular token ˜true™”whose meaning may indeed change as begingroup
a program runs”is not really involved. endgroup
numeric token primary
The special tokens ˜(™ and ˜)™ in these rules do not refer to parentheses; they
refer to any matching pair of delimiters de¬ned by a delimiters command.
A boolean variable denotes a variable whose type is boolean; a numeric
variable is a variable whose type is numeric; and so on. The syntax for variable
was discussed in Chapter 7. A boolean argument is an expr argument to a macro,
where the value of the expression is of type boolean; expr arguments are put into
special “capsule” tokens as explained in Chapter 18.
Numeric expressions have the richest syntax of all, because they form the
nucleus of the entire language:
numeric atom ’’ numeric variable | numeric argument
| numeric token primary
| internal quantity
| normaldeviate
| ( numeric expression )
| begingroup statement list numeric expression endgroup
numeric token primary ’’ numeric token / numeric token
| numeric token not followed by ˜/ numeric token ™
Chapter 25: Summary of Expressions 211

numeric primary ’’ numeric atom numeric primary
| numeric atom [ numeric expression , numeric expression ] [
| length numeric primary | length pair primary ]
| length path primary | length string primary length
| ASCII string primary | oct string primary | hex string primary length
| pair part pair primary | transform part transform primary length
| angle pair primary ASCII
| turningnumber path primary | totalweight picture primary hex
| numeric operator numeric primary angle
| directiontime pair expression of path primary turningnumber
pair part ’’ xpart | ypart directiontime
transform part ’’ pair part | xxpart | xypart | yxpart | yypart of
pair part
numeric operator ’’ sqrt | sind | cosd | mlog | mexp xpart
| floor | uniformdeviate | scalar multiplication operator ypart
scalar multiplication operator ’’ plus or minus transform part
| numeric token primary not followed by + or - or a numeric token xypart
numeric secondary ’’ numeric primary yxpart
| numeric secondary times or over numeric primary numeric operator
times or over ’’ * | / sqrt
numeric tertiary ’’ numeric secondary sind
| numeric tertiary plus or minus numeric secondary mlog
| numeric tertiary Pythagorean plus or minus numeric secondary mexp
plus or minus ’’ + | - uniformdeviate
Pythagorean plus or minus ’’ ++ | +-+ scalar multiplication operator
numeric expression ’’ numeric tertiary numeric secondary
times or over
Each of the operations mentioned in this syntax has already been explained somewhere /
in this book; Appendix I tells where. numeric tertiary
plus or minus
This is a good time to list all of the internal quantities that are initially present +
in Pythagorean plus or minus
tracingtitles show titles online when they appear +-+
tracingequations show each variable when it becomes known numeric expression
tracingcapsules show capsules as well as variables tracingequations
tracingchoices show the control points chosen for paths tracingcapsules
tracingspecs show subdivision of paths into octants before digitizing
tracingpens show vertices of pens as they are made from future pens tracingpens
tracingcommands show commands and operations before they™re performed tracingcommands
tracingrestores show when a symbol or internal quantity is restored
tracingmacros show macros before they are expanded tracingedges
tracingedges show digitized edges as they are computed tracingoutput
tracingoutput show digitized edges as they are output tracingstats
tracingonline show long diagnostics on the terminal and in the log pausing
tracingstats log the memory usage at end of job
pausing show lines on the terminal before they are read proo¬ng
showstopping stop after each show command
fontmaking produce font metric output
proo¬ng produce proof mode output
212 Chapter 25: Summary of Expressions

turningcheck reorient clockwise paths, ¬‚ag strange ones turningcheck
warningcheck advise when a variable value gets large
smoothing remove certain glitches from digitized curves autorounding
autorounding move paths to “good” tangent points granularity
granularity the pixel size for autorounding year
¬llin the extra darkness of diagonals (to be counteracted) month
year the current year (e.g., 1986)
the current month (e.g, 3 ≡ March)
month charcode
day the current day of the month charext
time the number of minutes past midnight when job started
charcode the number of the next character to be output chardp
charext the extension code of the next character to be output charic
charwd the width of the next character to be output, in points chardy
charht the height of the next character to be output, in points designsize
chardp the depth of the next character to be output, in points
charic the italic correction of the next character, in points xo¬set
chardx the device™s x movement for the next character, in pixels yo¬set
chardy the device™s y movement for the next character, in pixels
pair primary
designsize the approximate size of the current typeface, in points (
hppp the number of horizontal pixels per point ,
vppp the number of vertical pixels per point (
xo¬set the horizontal displacement of shipped-out characters )
yo¬set the vertical displacement of shipped-out characters
boundarychar the right boundary character for ligatures and kerns [
All of these quantities are numeric. They are initially zero at the start of a job, ]
except for year , month , day , and time , which are initialized to the time the run
began; furthermore, boundarychar is initially ’1. A granularity of zero is equivalent precontrol
to granularity = 1. A preloaded base ¬le like plain will usually give nonzero of
values to several other internal quantities on this list.
Now we come to expressions of type pair, which are the second most important of
elements of programs: peno¬set
pair primary ’’ pair variable | pair argument pair secondary
| ( numeric expression , numeric expression )
| ( pair expression )
| begingroup statement list pair expression endgroup
| numeric atom [ pair expression , pair expression ]
| scalar multiplication operator pair primary
| point numeric expression of path primary
| precontrol numeric expression of path primary
| postcontrol numeric expression of path primary
| penoffset pair expression of pen primary
| penoffset pair expression of future pen primary
pair secondary ’’ pair primary
| pair secondary times or over numeric primary
| numeric secondary * pair primary
| pair secondary transformer
Chapter 25: Summary of Expressions 213

transformer ’’ rotated numeric primary transformer
| scaled numeric primary rotated
| shifted pair primary shifted
| slanted numeric primary slanted
| transformed transform primary xscaled
| xscaled numeric primary yscaled
| yscaled numeric primary zscaled
pair tertiary
| zscaled pair primary intersectiontimes
pair tertiary ’’ pair secondary pair expression
| pair tertiary plus or minus pair secondary path primary
| path tertiary intersectiontimes path secondary )
pair expression ’’ pair tertiary begingroup
A pair is a special case of a path (namely, it™s a path of length zero); Chapter 19
explains that doesn™t change the type from pair to path unless there is no reverse
other way to meet the syntax rules. subpath
Speaking of paths, they come next in our survey: path secondary
path tertiary
path primary ’’ pair primary | path variable | path argument path subexpression
path join
| ( path expression ) direction speci¬er
| begingroup statement list path expression endgroup “
| makepath pen primary | makepath future pen primary curl
| reverse path primary “
| subpath pair expression of path primary ˝
path secondary ’’ pair secondary | path primary “
| path secondary transformer ˝
path tertiary ’’ pair tertiary | path secondary basic path join
path subexpression ’’ path expression ..
| path subexpression path join path tertiary ..
path join ’’ direction speci¬er basic path join direction speci¬er ..
direction speci¬er ’’ empty ..
| { curl numeric expression } tension
| { pair expression } tension
| { numeric expression , numeric expression } and
basic path join ’’ & tension amount
| .. controls
| .. tension .. controls
| .. controls .. controls
tension ’’ tension tension amount path expression
| tension tension amount and tension amount cycle
tension amount ’’ numeric primary
| atleast numeric primary
controls ’’ controls pair primary
| controls pair primary and pair primary
path expression ’’ pair expression | path tertiary
| path subexpression direction speci¬er
| path subexpression path join cycle
Chapter 14 tells all about path creation.
214 Chapter 25: Summary of Expressions

Pens and future pens coexist as follows: pen primary
pen primary ’’ pen variable | pen argument (
| nullpen begingroup
| ( pen expression ) endgroup
| begingroup statement list pen expression endgroup future pen primary
future pen primary ’’ pencircle makepen
| makepen path primary pen secondary
pen secondary ’’ pen primary future pen secondary
pen tertiary
future pen secondary ’’ future pen primary pen expression
| future pen secondary transformer picture primary
| pen secondary transformer (
pen tertiary ’’ pen secondary )
| future pen secondary begingroup
pen expression ’’ pen tertiary picture secondary
picture tertiary
See Chapter 16 for a thorough discussion of pen usage. picture expression
string primary
Pictures can be null, added, or subtracted: jobname
picture primary ’’ picture variable | picture argument (
| nullpicture )
| ( picture expression ) endgroup
| begingroup statement list picture expression endgroup str
| plus or minus picture primary char
picture secondary ’’ picture primary substring
| picture secondary transformer of
picture tertiary ’’ picture secondary string secondary
string tertiary
| picture tertiary plus or minus picture secondary string expression
picture expression ’’ picture tertiary &

Chapter 13 is the de¬nitive reference for picture operations.
Strings are still fresh in our minds from Chapter 22, but we should repeat the
syntax again for completeness here.
string primary ’’ string variable | string argument
| string token
| jobname
| readstring
| ( string expression )
| begingroup statement list string expression endgroup
| str su¬x
| char numeric primary
| decimal numeric primary
| substring pair primary of string primary
string secondary ’’ string primary
string tertiary ’’ string secondary
string expression ’’ string tertiary
| string expression & string tertiary
There™s nothing more to say about strings.
Chapter 25: Summary of Expressions 215

Chapter 15 explains transforms, but gives no formal syntax. The rules are: transform primary
transform primary ’’ transform variable | transform argument )
| ( transform expression ) endgroup
| begingroup statement list transform expression endgroup transform secondary
transform secondary ’’ transform primary transform tertiary
transform expression
| transform secondary transformer identity
transform tertiary ’’ transform secondary vacuous primary
transform expression ’’ transform tertiary (
Note that identity doesn™t appear here; it is a variable de¬ned in Appendix B, not a endgroup
primitive of the language. vacuous secondary
vacuous tertiary
Finally, we come to the new kind of expression, which wasn™t mentioned in vacuous expression
previous chapters because it is so trivial.
vacuous primary ’’ vacuous argument
| compound
| ( vacuous expression )
| begingroup statement list vacuous expression endgroup
vacuous secondary ’’ vacuous primary
vacuous tertiary ’’ vacuous secondary
vacuous expression ’’ vacuous tertiary
A compound is de¬ned in Chapter 26.
Construct minimal examples of each of the nine types of expression (boolean,
numeric, . . . , vacuous). You should use only “sparks” in your constructions, not tag
tokens or capsules; in particular, variables are not permitted (otherwise this exercise
would be too easy). Your expressions should be as short as possible in the sense of
fewest tokens; the number of keystrokes needed to type them is irrelevant.

This is of you very well remembred,
and well and sommaryly rehersed.
” THOMAS MORE, A Dialogue Concernynge Heresyes (1529)

Below the tomato blobs was a band of white with vertical black stripes,
to which he could assign no meaning whatever,
till some one else came by, murmuring:
“What expression he gets with his foreground!”
. . . Ah, they were all Expressionists now, he had heard, on the Continent.
So it was coming here too, was it?
” JOHN GALSWORTHY, To Let (1921)
(page 216)

Summary of
the Language
Chapter 26: Summary of the Language 217

The grand tour of ™s syntax that was begun in the previous chapter truth
anatomy of METAFONT
is concluded in this one, so that a complete reference guide is available for people program
who need to know the details. (Another summary appears in Appendix B.) end
statement list
actually has a few features that didn™t seem to be worth men-
tioning in earlier chapters, so they will be introduced here as part of our exhaustive statement
survey. If there is any disagreement between something that was said previously and title
something that will be said below, the facts in the present chapter should be regarded
as better approximations to the truth. endgroup
We shall study ™s digestive processes, i.e., what does command
in response to the tokens that arrive in its “stomach.” Chapter 6 describes the process
by which input ¬les are converted to lists of tokens in ™s “mouth,” and
Chapters 18“20 explain how expandable tokens are converted to unexpandable ones in
™s “gullet” by a process similar to regurgitation. In particular, conditions
and loops are handled by the expansion mechanism, and we need not discuss them
further. When unexpandable tokens ¬nally reach ™s gastro-intestinal tract,
the real activities begin; expressions are evaluated, equations are solved, variables are
declared, and commands are executed. In this chapter we shall discuss the primitive
operations that actually draw pictures and produce output.
Let™s start by looking at the full syntax for program and for statement :
program ’’ statement list end | statement list dump
statement list ’’ empty | statement ; statement list
statement ’’ empty | title
| equation | assignment
| declaration | de¬nition
| compound | command
title ’’ string expression
compound ’’ begingroup statement list non-title statement endgroup
command ’’ save command
| interim command
| newinternal command
| randomseed command
| let command
| delimiters command
| protection command
| everyjob command
| show command
| message command
| mode command
| picture command
| display command
| openwindow command
| shipout command
| special command
| font metric command
The empty statement does nothing, but it is very handy because you can always feel
safe when you put extra semicolons between statements. A title does almost noth-
218 Chapter 26: Summary of the Language

ing, but it provides useful documentation as explained in Chapter 22. The syntax of commands
save command
equation and assignment can be found in Chapter 10; declaration is in Chapter 7;
de¬nition is in Chapters 18 and 20. We shall concentrate in this chapter on the symbolic token list
various types of commands, especially on those that haven™t been mentioned before. ,
interim command
save command ’’ save symbolic token list interim
symbolic token list ’’ symbolic token newinternal command
| symbolic token list , symbolic token newinternal
interim command ’’ interim internal quantity := right-hand side external tags
randomseed command
The save and interim commands cause values to be restored at the end of the current randomseed
group, as discussed in Chapter 17. :=
newinternal command ’’ newinternal symbolic token list time
let command
Each of the symbolic tokens speci¬ed in a newinternal command will henceforth
behave exactly as an internal quantity , initially zero. Thus, they can be used in =
interim commands; they are tags but not external tags (see Chapter 7). Since - :=
delimiters command
can access internal quantities quickly, you can use them to gain e¬ciency. delimiters
protection command
randomseed command ’’ randomseed := numeric expression outer
The randomseed command speci¬es a “seed” value that de¬nes the pseudo-random forbidden
numbers to be delivered by ˜uniformdeviate™ and ˜normaldeviate™ (cf. Chapter 21). The
default value, if you don™t specify your own seed, is day + time — epsilon .
let command ’’ let symbolic token is symbolic token
is ’’ = | :=
The let command changes the current meaning of the left-hand token to the current
meaning of the right-hand token. For example, after ˜let diamonds = forever™, the
token diamonds will introduce loops. If the left-hand token was the ¬rst token of
any variable names, those variables all disappear. If the right-hand token was the ¬rst
token in any variable names, those variables remain unchanged, and the left-hand token
becomes an unknown, independent variable. (The purpose of let is to rede¬ne primitive
meanings or macro meanings, not to equate variables in any way.) If the right-hand
symbol is one of a pair of matching delimiters, the subsequent behavior of the left-hand
symbol is unde¬ned. For example, it™s a bad idea to say ˜let [ [= (; let ] ] =)™.
delimiters command ’’ delimiters symbolic token symbolic token
The delimiters command gives new meanings to the two symbolic tokens; henceforth
they will match each other (and only each other). For example, Appendix B says
˜delimiters ()™; without this command, parentheses would be ordinary symbolic tokens.
Any distinct symbolic tokens can be de¬ned to act as delimiters, and many di¬erent
pairs of delimiters can be in use simultaneously.
protection command ’’ outer symbolic token list
| inner symbolic token list
A “forbidden” stamp is added to or removed from symbolic tokens by an outer or inner
command, without changing the essential meanings of those tokens. A token that has
been called outer should not appear when is skipping over tokens at high
Chapter 26: Summary of the Language 219

speed; the program will stop and insert an appropriate delimiter, if an outer token condition
replacement text
is sensed in the wrong place, since such tokens are supposed to occur only at “quiet”
loop text
times. (Unquiet times occur when is skipping tokens because of a false text argument
condition, or because it is reading the replacement text of a macro or the loop text of ¬‚ushing
everyjob command
a loop, or because it is scanning the text argument to a macro, or because it is ¬‚ushing everyjob
erroneous tokens that were found at the end of a statement.) Without such protection, show command
a missing right delimiter could cause to eat up your whole program before
any error was detected; outer tokens keep such errors localized. An inner command showtoken
undoes the e¬ect of outer; so does ˜let™, and so does any other command or de¬nition showdependencies
that changes the meaning of a symbolic token. All tokens are initially inner.
everyjob command ’’ everyjob symbolic token OK.
The command ˜everyjob S™ tells that token S should be inserted ¬rst, message command
just before the input ¬le is read, when a job starts. (This is meaningful only in a base message op
¬le that will be loaded or preloaded at the beginning of a run; it is analogous to TEX™s
\everyjob command.) errhelp
mode command
show command ’’ show expression list batchmode
| showvariable symbolic token list nonstopmode
| showtoken symbolic token list errorstopmode
| showdependencies interrupting
| showstats
A simple show command displays the value of each expression, in turn. Paths, pens,
and pictures are shown only in the transcript ¬le, unless tracingonline is positive.
The showvariable command gives the structure of all variables that begin with a
given external tag, together with their values in an abbreviated form; this allows you
to see which of its subscripts and attributes have occurred. For example, if you™re
using plain conventions, ˜showvariable x, y™ will show all coordinates
that have been de¬ned since the last beginchar. The showtoken command gives
the current meaning of a token, so that you can tell whether it is primitive or not,
outer or not. (If showvariable is applied to a spark instead of a tag, it gives the
same information as showtoken.) Every unknown numeric variable that™s currently
dependent is shown by showdependencies (except that unknown capsules are shown
only when tracingcapsules is positive). And ¬nally, showstats gives information about
™s current memory usage. Each of these commands will stop and say ˜! OK.™,
if the internal quantity showstopping has a positive value; this gives you a chance to
enter more show commands interactively, while you™re trying to debug a program.
message command ’’ message op string expression
message op ’’ message | errmessage | errhelp
Communication with the user is possible via message, errmessage, and errhelp, as
discussed in Chapter 22.
mode command ’’ batchmode | nonstopmode


. 7
( 13)