102  INTRODUCTION  VFtoVP §1

1. Introduction. The utility program converts a virtual font (“ ”) file and its associated TEX font metric (“ ”) file into an equivalent virtual-property-list (“ ”) file. It also makes a thorough check of the given files, using algorithms that are essentially the same as those used by device drivers and by TEX. Thus if TEX or a driver complains that a or file is “bad,” this program will pinpoint the source or sources of badness. A file output by this program can be edited with a normal text editor, and the result can be converted back to and format using the companion program .

is an extended version of the program , which is part of the standard TEXware library. The idea of a virtual font was inspired by the work of David R. Fuchs who designed a similar set of conventions in 1984 while developing a device driver for ArborText, Inc. He wrote a somewhat similar program called .

The banner string defined here should be changed whenever gets modified. define banner ≡ ´ ␣ ␣ ␣ ␣ ´ { printed when the program starts }

2. This program is written entirely in standard Pascal, except that it occasionally has lower case letters in strings that are output. Such letters can be converted to upper case if necessary. The input is read from vf file and tfm file; the output is written on vpl file. Error messages and other remarks are written on the output file, which the user may choose to assign to the terminal if the system permits it.

The term print is used instead of write when this program writes on the output file, so that all such output can be easily deflected.

define print (#) ≡ write(#) define print ln(#) ≡ write ln(#)

program VFtoVP (vf file, tfm file, vpl file, output ); label ⟨ Labels in the outer block 3 ⟩ const ⟨ Constants in the outer block 4 ⟩ type ⟨ Types in the outer block 5 ⟩ var ⟨ Globals in the outer block 7 ⟩ procedure initialize; { this procedure gets things started properly }

var k: integer ; { all-purpose index for initialization } begin print ln(banner ); ⟨ Set initial values 11 ⟩ end;

3. If the program has to stop prematurely, it goes to the ‘final end ’.

define final end = 9999 { label for the end of it all } ⟨ Labels in the outer block 3 ⟩ ≡

final end ;

This code is used in section 2.

4. The following parameters can be changed at compile time to extend or reduce ’s capacity. ⟨ Constants in the outer block 4 ⟩ ≡

tfm size = 30000; { maximum length of tfm data, in bytes } vf size = 10000; { maximum length of vf data, in bytes } max fonts = 300; { maximum number of local fonts in the vf file } lig size = 5000; { maximum length of lig kern program, in words } hash size = 5003;

{ preferably a prime number, a bit larger than the number of character pairs in lig/kern steps } name length = 50; { a file name shouldn’t be longer than this } max stack = 50; { maximum depth of stack in character packets }

This code is used in section 2.

§5  VFtoVP  INTRODUCTION 103

5. Here are some macros for common programming idioms.

define incr (#) ≡ # ← # + 1 { increase a variable by unity } define decr (#) ≡ # ← # − 1 { decrease a variable by unity } define do nothing ≡ { empty statement } define exit = 10 { go here to leave a procedure } define not found = 45 { go here when you’ve found nothing } define return ≡ goto exit { terminate a procedure call } format return ≡ nil

⟨ Types in the outer block 5 ⟩ ≡

byte = 0 . . 255; { unsigned eight-bit quantity }

See also section 22. This code is used in section 2.

104  VIRTUAL FONTS  VFtoVP §6

6. Virtual fonts. The idea behind files is that a general interface mechanism is needed to switch between the myriad font layouts provided by different suppliers of typesetting equipment. Without such a mechanism, people must go to great lengths writing inscrutable macros whenever they want to use typesetting conventions based on one font layout in connection with actual fonts that have another layout. This puts an extra burden on the typesetting system, interfering with the other things it needs to do (like kerning, hyphenation, and ligature formation).

These difficulties go away when we have a “virtual font,” i.e., a font that exists in a logical sense but not a physical sense. A typesetting system like TEX can do its job without knowing where the actual characters come from; a device driver can then do its job by letting a file tell what actual characters correspond to the characters TEX imagined were present. The actual characters can be shifted and/or magnified and/or combined with other characters from many different fonts. A virtual font can even make use of characters from virtual fonts, including itself.

Virtual fonts also allow convenient character substitutions for proofreading purposes, when fonts designed for one output device are unavailable on another.

7. A file is organized as a stream of 8-bit bytes, using conventions borrowed from and files. Thus, a device driver that knows about and format will already contain most of the mechanisms necessary to process files. We shall assume that format is understood; the conventions in the documentation (see, for example, TEX: The Program, part 31) are adopted here to define format.

A preamble appears at the beginning, followed by a sequence of character definitions, followed by a postamble. More precisely, the first byte of every file must be the first byte of the following “preamble command”:

pre 247 i[1] k[1] x[k] cs [4] ds [4]. Here i is the identification byte of , currently 202. The string x is merely a comment, usually indicating the source of the file. Parameters cs and ds are respectively the check sum and the design size of the virtual font; they should match the first two words in the header of the file, as described below.

After the pre command, the preamble continues with font definitions; every font needed to specify “actual” characters in later set char commands is defined here. The font definitions are exactly the same in files as they are in files, except that the scaled size s is relative and the design size d is absolute:

fnt def1 243 k[1] c[4] s[4] d[4] a[1] l[1] n[a + l]. Define font k, where 0 ≤ k < 256.

fnt def2 244 k[2] c[4] s[4] d[4] a[1] l[1] n[a + l]. Define font k, where 0 ≤ k < 65536.

fnt def3 245 k[3] c[4] s[4] d[4] a[1] l[1] n[a + l]. Define font k, where 0 ≤ k < 2^{24}.

fnt def4 246 k[4] c[4] s[4] d[4] a[1] l[1] n[a + l]. Define font k, where −2^{31} ≤ k < 2^{31}. These font numbers k are “local”; they have no relation to font numbers defined in the file that uses this virtual font. The dimension s, which represents the scaled size of the local font being defined, is a fix word relative to the design size of the virtual font. Thus if the local font is to be used at the same size as the design size of the virtual font itself, s will be the integer value 2^{20}. The value of s must be positive and less than 2^{24} (thus less than 16 when considered as a fix word ). The dimension d is a fix word in units of printer’s points; hence it is identical to the design size found in the corresponding file.

define id byte = 202 ⟨ Globals in the outer block 7 ⟩ ≡ vf file: packed file of byte;

See also sections 10, 12, 20, 23, 26, 29, 30, 37, 42, 49, 51, 54, 67, 69, 85, 87, 111, and 123. This code is used in section 2.

§8  VFtoVP  VIRTUAL FONTS 105

8. The preamble is followed by zero or more character packets, where each character packet begins with a byte that is < 243. Character packets have two formats, one long and one short:

long char 242 pl [4] cc[4] tfm[4] dvi [pl ]. This long form specifies a virtual character in the general case. short char0 . . short char241 pl [1] cc[1] tfm[3] dvi [pl ]. This short form specifies a virtual character in the

common case when 0 ≤ pl < 242 and 0 ≤ cc < 256 and 0 ≤ tfm < 2^{24}. Here pl denotes the packet length following the tfm value; cc is the character code; and tfm is the character width copied from the file for this virtual font. There should be at most one character packet having any given cc code.

The dvi bytes are a sequence of complete commands, properly nested with respect to push and pop. All operations are permitted except bop, eop, and commands with opcodes ≥ 243. Font selection commands (fnt num0 through fnt4 ) must refer to fonts defined in the preamble.

Dimensions that appear in the instructions are analogous to fix word quantities; i.e., they are integer multiples of 2^{−20} times the design size of the virtual font. For example, if the virtual font has design size 10 pt, the command to move down 5 pt would be a down instruction with parameter 2^{19}. The virtual font itself might be used at a different size, say 12 pt; then that down instruction would move down 6 pt instead. Each dimension must be less than 2^{24} in absolute value.

Device drivers processing files treat the sequences of dvi bytes as subroutines or macros, implicitly enclosing them with push and pop. Each subroutine begins with w = x = y = z = 0, and with current font f the number of the first-defined in the preamble (undefined if there’s no such font). After the dvi commands have been performed, the h and v position registers of format and the current font f are restored to their former values; then, if the subroutine has been invoked by a set char or set command, h is increased by the width (properly scaled)—just as if a simple character had been typeset.

define long char = 242 { command for general character packet } define set char 0 = 0 { command to typeset character 0 and move right } define set1 = 128 { typeset a character and move right } define set rule = 132 { typeset a rule and move right } define put1 = 133 { typeset a character } define put rule = 137 { typeset a rule } define nop = 138 { no operation } define push = 141 { save the current positions } define pop = 142 { restore previous positions } define right1 = 143 { move right } define w0 = 147 { move right by w } define w1 = 148 { move right and set w } define x0 = 152 { move right by x } define x1 = 153 { move right and set x } define down1 = 157 { move down } define y0 = 161 { move down by y } define y1 = 162 { move down and set y } define z0 = 166 { move down by z } define z1 = 167 { move down and set z } define fnt num 0 = 171 { set current font to 0 } define fnt1 = 235 { set current font } define xxx1 = 239 { extension to primitives } define xxx4 = 242 { potentially long extension to primitives } define fnt def1 = 243 { define the meaning of a font number } define pre = 247 { preamble } define post = 248 { postamble beginning } define improper DVI for VF ≡ 139, 140, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 9. The character packets are followed by a trivial postamble, consisting of one or more bytes all equal to post (248). The total number of bytes in the file should be a multiple of 4.

§10  VFtoVP  FONT METRIC DATA 107

10. Font metric data. The idea behind files is that typesetting routines like TEX need a compact way to store the relevant information about several dozen fonts, and computer centers need a compact way to store the relevant information about several hundred fonts. files are compact, and most of the information they contain is highly relevant, so they provide a solution to the problem.

The information in a file appears in a sequence of 8-bit bytes. Since the number of bytes is always a multiple of 4, we could also regard the file as a sequence of 32-bit words; but TEX uses the byte interpretation, and so does . Note that the bytes are considered to be unsigned numbers. ⟨ Globals in the outer block 7 ⟩ +≡ tfm file: packed file of byte;

11. On some systems you may have to do something special to read a packed file of bytes. For example, the following code didn’t work when it was first tried at Stanford, because packed files have to be opened with a special switch setting on the Pascal that was used. ⟨ Set initial values 11 ⟩ ≡

reset (tfm file); reset (vf file);

See also sections 21, 43, 50, 55, 68, and 86. This code is used in section 2.

12. The first 24 bytes (6 words) of a file contain twelve 16-bit integers that give the lengths of the various subsequent portions of the file. These twelve integers are, in order:

lf = length of the entire file, in words;
lh = length of the header data, in words;
bc = smallest character code in the font;
ec = largest character code in the font;
nw = number of words in the width table;
nh = number of words in the height table;
nd = number of words in the depth table;
ni = number of words in the italic correction table;
nl = number of words in the lig/kern table;
nk = number of words in the kern table;
ne = number of words in the extensible character table;
np = number of font parameter words.

They are all nonnegative and less than 2^{15}. We must have bc − 1 ≤ ec ≤ 255, ne ≤ 256, and

lf = 6 + lh + (ec − bc + 1) + nw + nh + nd + ni + nl + nk + ne + np.

Note that a font may contain as many as 256 characters (if bc = 0 and ec = 255), and as few as 0 characters (if bc = ec + 1).

108  FONT METRIC DATA  VFtoVP §13

Incidentally, when two or more 8-bit bytes are combined to form an integer of 16 or more bits, the most significant bytes appear first in the file. This is called BigEndian order. ⟨ Globals in the outer block 7 ⟩ +≡ lf , lh, bc, ec, nw , nh, nd , ni , nl , nk , ne, np: 0 . . ´ 77777 ; { subfile sizes } 13. The rest of the file may be regarded as a sequence of ten data arrays having the informal specification

header : array [0 . . lh − 1] of stuff
char info : array [bc . . ec] of char info word
width : array [0 . . nw − 1] of fix word
height : array [0 . . nh − 1] of fix word
depth : array [0 . . nd − 1] of fix word
italic : array [0 . . ni − 1] of fix word
lig kern : array [0 . . nl − 1] of lig kern command
kern : array [0 . . nk − 1] of fix word
exten : array [0 . . ne − 1] of extensible recipe
param : array [1 . . np] of fix word

The most important data type used here is a fix word , which is a 32-bit representation of a binary fraction. A fix word is a signed quantity, with the two’s complement of the entire word used to represent negation. Of the 32 bits in a fix word , exactly 12 are to the left of the binary point; thus, the largest fix word value is 2048 − 2^{−20}, and the smallest is −2048. We will see below, however, that all but one of the fix word values will lie between −16 and +16.

14. The first data array is a block of header information, which contains general facts about the font. The header must contain at least two words, and for files to be used with Xerox printing software it must contain at least 18 words, allocated as described below. When different kinds of devices need to be interfaced, it may be necessary to add further words to the header block.

header [0] is a 32-bit check sum that TEX will copy into the output file whenever it uses the font. Later on when the file is printed, possibly on another computer, the actual font that gets used is supposed to have a check sum that agrees with the one in the file used by TEX. In this way, users will be warned about potential incompatibilities. (However, if the check sum is zero in either the font file or the file, no check is made.) The actual relation between this check sum and the rest of the file is not important; the check sum is simply an identification number with the property that incompatible fonts almost always have distinct check sums.

header [1] is a fix word containing the design size of the font, in units of TEX points (7227 TEX points = 254 cm). This number must be at least 1.0; it is fairly arbitrary, but usually the design size is 10.0 for a “10 point” font, i.e., a font that was designed to look best at a 10-point size, whatever that really means. When a TEX user asks for a font ‘ δ ’, the effect is to override the design size and replace it by δ, and to multiply the x and y coordinates of the points in the font image by a factor of δ divided by the design size. All other dimensions in the file are fix word numbers in design-size units. Thus, for example, the value of param[6], one or , is often the fix word value 2^{20} = 1.0, since many fonts have a design size equal to one em. The other dimensions must be less than 16 design-size units in absolute value; thus, header [1] and param[1] are the only fix word entries in the whole file whose first byte might be something besides 0 or 255.

header [2 . . 11], if present, contains 40 bytes that identify the character coding scheme. The first byte, which must be between 0 and 39, is the number of subsequent ASCII bytes actually relevant in this string, which is intended to specify what character-code-to-symbol convention is present

in the font. Examples are  for standard ASCII,  for fonts like  and  ,
for  ,  for Xerox fonts,  for special-purpose non-

alphabetic fonts, for the default case when there is no information. Parentheses should not appear in this name. (Such a string is said to be in BCPL format.)

header [12 . . 16], if present, contains 20 bytes that name the font family (e.g., or ), in BCPL

format. This field is also known as the “font identifier.” header [17], if present, contains a first byte called the seven bit safe flag , then two bytes that are ignored,

and a fourth byte called the face. If the value of the fourth byte is less than 18, it has the following interpretation as a “weight, slope, and expansion”: Add 0 or 2 or 4 (for medium or bold or light) to 0 or 1 (for roman or italic) to 0 or 6 or 12 (for regular or condensed or extended). For example, 13 is 0+1+12, so it represents medium italic extended. A three-letter code (e.g., ) can be used for such face data.

header [18 . . whatever] might also be present; the individual words are simply called header [18], header [19], etc., at the moment.

15. Next comes the char info array, which contains one char info word per character. Each char info word contains six fields packed into four bytes as follows.

first byte: width index (8 bits) second byte: height index (4 bits) times 16, plus depth index (4 bits) third byte: italic index (6 bits) times 4, plus tag (2 bits) fourth byte: remainder (8 bits)

The actual width of a character is width[width index ], in design-size units; this is a device for compressing information, since many characters have the same width. Since it is quite common for many characters to have the same height, depth, or italic correction, the format imposes a limit of 16 different heights, 16 different depths, and 64 different italic corrections.

Incidentally, the relation width[0] = height [0] = depth[0] = italic[0] = 0 should always hold, so that an index of zero implies a value of zero. The width index should never be zero unless the character does not exist in the font, since a character is valid if and only if it lies between bc and ec and has a nonzero width index .

16. The tag field in a char info word has four values that explain how to interpret the remainder field. tag = 0 (no tag ) means that remainder is unused. tag = 1 (lig tag ) means that this character has a ligature/kerning program starting at lig kern[remainder ]. tag = 2 (list tag ) means that this character is part of a chain of characters of ascending sizes, and not the

largest in the chain. The remainder field gives the character code of the next larger character. tag = 3 (ext tag ) means that this character code represents an extensible character, i.e., a character that

is built up of smaller pieces so that it can be made arbitrarily large. The pieces are specified in exten[remainder ].

define no tag = 0 { vanilla character } define lig tag = 1 { character has a ligature/kerning program } define list tag = 2 { character has a successor in a charlist } define ext tag = 3 { character is extensible } 17. The lig kern array contains instructions in a simple programming language that explains what to do for special letter pairs. Each word is a lig kern command of four bytes.

first byte: skip byte, indicates that this is the final program step if the byte is 128 or more, otherwise the

next step is obtained by skipping this number of intervening steps. second byte: next char , “if next char follows the current character, then perform the operation and stop,

otherwise continue.” third byte: op byte, indicates a ligature step if less than 128, a kern step otherwise. fourth byte: remainder .

In a kern step, an additional space equal to kern[256 ∗ (op byte − 128) + remainder ] is inserted between the current character and next char . This amount is often negative, so that the characters are brought closer together by kerning; but it might be positive.

There are eight kinds of ligature steps, having op byte codes 4a+2b+c where 0 ≤ a ≤ b+c and 0 ≤ b, c ≤ 1. The character whose code is remainder is inserted between the current character and next char ; then the current character is deleted if b = 0, and next char is deleted if c = 0; then we pass over a characters to reach the next current character (which may have a ligature/kerning program of its own).

Notice that if a = 0 and b = 1, the current character is unchanged; if a = b and c = 1, the current character is changed but the next character is unchanged. will check to see that infinite loops are avoided.

If the very first instruction of the lig kern array has skip byte = 255, the next char byte is the so-called right boundary character of this font; the value of next char need not lie between bc and ec. If the very last instruction of the lig kern array has skip byte = 255, there is a special ligature/kerning program for a left boundary character, beginning at location 256 ∗ op byte + remainder . The interpretation is that TEX puts implicit boundary characters before and after each consecutive string of characters from the same font. These implicit characters do not appear in the output, but they can affect ligatures and kerning.

If the very first instruction of a character’s lig kern program has skip byte > 128, the program actually begins in location 256 ∗ op byte + remainder . This feature allows access to large lig kern arrays, because the first instruction must otherwise appear in a location ≤ 255.

Any instruction with skip byte > 128 in the lig kern array must have 256 ∗ op byte + remainder < nl . If such an instruction is encountered during normal program execution, it denotes an unconditional halt; no ligature command is performed.

define stop flag = 128 { value indicating ‘ ’ in a lig/kern program }

define kern flag = 128 { op code for a kern step }

18. Extensible characters are specified by an extensible recipe, which consists of four bytes called top, mid , bot , and rep (in this order). These bytes are the character codes of individual pieces used to build up a large symbol. If top, mid , or bot are zero, they are not present in the built-up result. For example, an extensible vertical line is like an extensible bracket, except that the top and bottom pieces are missing.

19. The final portion of a file is the param array, which is another sequence of fix word values.

param[1] = slant is the amount of italic slant, which is used to help position accents. For example, slant = .25 means that when you go up one unit, you also go .25 units to the right. The slant is a pure number; it’s the only fix word other than the design size itself that is not scaled by the design size.

param[2] = space is the normal spacing between words in text. Note that character ␣ in the font need

not have anything to do with blank spaces. param[3] = space stretch is the amount of glue stretching between words. param[4] = space shrink is the amount of glue shrinking between words. param[5] = x height is the height of letters for which accents don’t have to be raised or lowered. param[6] = quad is the size of one em in the font. param[7] = extra space is the amount added to param[2] at the ends of sentences. When the character coding scheme is , the font is supposed to have 15 additional

parameters called num1 , num2 , num3 , denom1 , denom2 , sup1 , sup2 , sup3 , sub1 , sub2 , supdrop, subdrop, delim1 , delim2 , and axis height , respectively. When the character coding scheme is , the font is supposed to have six additional parameters called default rule thickness and big op spacing1 through big op spacing5 .

20. So that is what  files hold. The next question is, “What about  files?” A complete answer to

that question appears in the documentation of the companion program, , so it will not be repeated here. Suffice it to say that a file is an ordinary Pascal text file, and that the output of uses only a subset of the possible constructions that might appear in a file. Furthermore, hardly anybody really wants to look at the formal definition of format, because it is almost self-explanatory when you see an example or two. ⟨ Globals in the outer block 7 ⟩ +≡ vpl file: text ;

§22  VFtoVP  UNPACKING THE TFM FILE 113

21. ⟨ Set initial values 11 ⟩ +≡ rewrite(vpl file); 22. Unpacking the TFM file. The first thing does is read the entire tfm file into an array of bytes, tfm[0 . . (4 ∗ lf − 1)]. ⟨ Types in the outer block 5 ⟩ +≡

index = 0 . . tfm size; { address of a byte in tfm }

23. ⟨ Globals in the outer block 7 ⟩ +≡ tfm: array [−1000 . . tfm size] of byte; { the input data all goes here }

{ the negative addresses avoid range checks for invalid characters }

24. The input may, of course, be all screwed up and not a file at all. So we begin cautiously.

define abort (#) ≡ begin print ln(#); print ln(´ ␣ ␣ ␣ ´ ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); goto final end ; end

⟨ Read the whole file 24 ⟩ ≡ read (tfm file, tfm[0]); if tfm[0] > 127 then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); if eof (tfm file) then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); read (tfm file, tfm[1]); lf ← tfm[0] ∗ ´ 400 + tfm[1]; if lf = 0 then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ´ ␣ ´ ); if 4 ∗ lf − 1 > tfm size then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); for tfm ptr ← 2 to 4 ∗ lf − 1 do

begin if eof (tfm file) then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); read (tfm file, tfm[tfm ptr ]); end;

if ¬eof (tfm file) then

begin print ln(´  ´  ´ ␣    ␣     ␣    ␣  ␣   ␣   ␣  ␣   ␣   ␣  ´ );

  print ln(´ ␣ ´  ´ ␣       ␣  ␣  ␣  ␣     ´       ´ ␣  ´ );

end

This code is used in section 131.

114  UNPACKING THE TFM FILE  VFtoVP §25

25. After the file has been read successfully, we look at the subfile sizes to see if they check out.

define eval two bytes (#) ≡ begin if tfm[tfm ptr ] > 127 then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); # ← tfm[tfm ptr ] ∗ ´ 400 + tfm[tfm ptr + 1]; tfm ptr ← tfm ptr + 2; end

⟨ Set subfile sizes lh, bc, …, np 25 ⟩ ≡ begin tfm ptr ← 2; eval two bytes (lh); eval two bytes (bc); eval two bytes (ec); eval two bytes (nw ); eval two bytes (nh); eval two bytes (nd ); eval two bytes (ni ); eval two bytes (nl ); eval two bytes (nk ); eval two bytes (ne); eval two bytes (np); if lh < 2 then abort (´ ␣ ␣ ␣ ␣ ␣´ , lh : 1, ´ ´ ); if nl > 4 ∗ lig size then abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); if (bc > ec + 1) ∨ (ec > 255) then

abort (´ ␣ ␣ ␣ ␣´ , bc : 1, ´ ´ , ec : 1, ´ ␣ ´ ); if (nw = 0) ∨ (nh = 0) ∨ (nd = 0) ∨ (ni = 0) then

abort (´ ␣ ␣ ␣ ␣ ´ ); if ne > 256 then abort (´ ␣ ␣´ , ne : 1, ´ ␣ ␣ ´ ); if lf ≠ 6 + lh + (ec − bc + 1) + nw + nh + nd + ni + nl + nk + ne + np then

  abort (´ ␣     ␣   ´     ´ ␣   ␣  ␣  ␣   ␣      ␣  ´ );

end

This code is used in section 131.

26. Once the input data successfully passes these basic checks,  believes that it is a  file, and

the conversion to format will take place. Access to the various subfiles is facilitated by computing the following base addresses. For example, the char info for character c will start in location 4 ∗ (char base + c) of the tfm array. ⟨ Globals in the outer block 7 ⟩ +≡ char base, width base, height base, depth base, italic base, lig kern base, kern base, exten base, param base:

integer ; { base addresses for the subfiles }

27. ⟨ Compute the base addresses 27 ⟩ ≡ begin char base ← 6 + lh − bc; width base ← char base + ec + 1; height base ← width base + nw ; depth base ← height base + nh; italic base ← depth base + nd ; lig kern base ← italic base + ni ; kern base ← lig kern base + nl ; exten base ← kern base + nk ; param base ← exten base + ne − 1; end

This code is used in section 131.

28. Of course we want to define macros that suppress the detail of how the font information is actually encoded. Each word will be referred to by the tfm index of its first byte. For example, if c is a character code between bc and ec, then tfm[char info(c)] will be the first byte of its char info, i.e., the width index ; furthermore width(c) will point to the fix word for c’s width.

define check sum = 24 define design size = check sum + 4 define scheme = design size + 4 define family = scheme + 40 define random word = family + 20 define char info(#) ≡ 4 ∗ (char base + #) define width index (#) ≡ tfm[char info(#)] define nonexistent (#) ≡ ((# < bc) ∨ (# > ec) ∨ (width index (#) = 0)) define height index (#) ≡ (tfm[char info(#) + 1] div 16) define depth index (#) ≡ (tfm[char info(#) + 1] mod 16) define italic index (#) ≡ (tfm[char info(#) + 2] div 4) define tag (#) ≡ (tfm[char info(#) + 2] mod 4) define reset tag (#) ≡ tfm[char info(#) + 2] ← 4 ∗ italic index (#) + no tag define remainder (#) ≡ tfm[char info(#) + 3] define width(#) ≡ 4 ∗ (width base + width index (#)) define height (#) ≡ 4 ∗ (height base + height index (#)) define depth(#) ≡ 4 ∗ (depth base + depth index (#)) define italic(#) ≡ 4 ∗ (italic base + italic index (#)) define exten(#) ≡ 4 ∗ (exten base + remainder (#)) define lig step(#) ≡ 4 ∗ (lig kern base + (#)) define kern(#) ≡ 4 ∗ (kern base + #) { here # is an index, not a character } define param(#) ≡ 4 ∗ (param base + #) { likewise }

29. One of the things we would like to do is take cognizance of fonts whose character coding scheme is or ; we will set the font type variable to one of the three choices vanilla, mathsy , or mathex .

define vanilla = 0 { not a special scheme } define mathsy = 1 { scheme } define mathex = 2 { scheme }

⟨ Globals in the outer block 7 ⟩ +≡ font type: vanilla . . mathex ; { is this font special? }

116  UNPACKING THE VF FILE  VFtoVP §30
30. Unpacking the VF file. Once the  file has been brought into memory,  completes the

input phase by reading the information into another array of bytes. In this case we don’t store all the data; we check the redundant bytes for consistency with their counterparts, and we partially decode the packets. ⟨ Globals in the outer block 7 ⟩ +≡ vf : array [0 . . vf size] of byte; { the input data goes here } font number : array [0 . . max fonts ] of integer ; { local font numbers } font start , font chars : array [0 . . max fonts ] of 0 . . vf size; { font info } font ptr : 0 . . max fonts ; { number of local fonts } packet start , packet end : array [byte] of 0 . . vf size; { character packet boundaries } packet found : boolean; { at least one packet has appeared } temp byte: byte; count : integer ; { registers for simple calculations } real dsize: real ; { the design size, converted to floating point } pl : integer ; { packet length } vf ptr : 0 . . vf size; { first unused location in vf } vf count : integer ; { number of bytes read from vf file }

31. Again we cautiously verify that we’ve been given decent data.

define read vf (#) ≡ read (vf file, #)

define vf abort (#) ≡ begin print ln(#); print ln(´ ␣ ␣ ␣ ´ ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); goto final end ; end

⟨ Read the whole file 31 ⟩ ≡ read vf (temp byte); if temp byte ≠ pre then vf abort (´ ␣ ␣ ␣ ´ ´ ␣` ´ ´ ´ ); ⟨ Read the preamble command 32 ⟩; ⟨ Read and store the font definitions and character packets 33 ⟩; ⟨ Read and verify the postamble 34 ⟩

This code is used in section 131.

§32  VFtoVP  UNPACKING THE VF FILE 117

32. define vf store(#) ≡ if vf ptr + # ≥ vf size then vf abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); for k ← vf ptr to vf ptr + # − 1 do

begin if eof (vf file) then vf abort (´ ␣ ␣ ␣ ´ ); read vf (vf [k]); end;

vf count ← vf count + #; vf ptr ← vf ptr + #

⟨ Read the preamble command 32 ⟩ ≡ if eof (vf file) then vf abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); read vf (temp byte); if temp byte ≠ id byte then vf abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); if eof (vf file) then vf abort (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); read vf (temp byte); { read the length of introductory comment } vf count ← 11; vf ptr ← 0; vf store(temp byte); for k ← 0 to vf ptr − 1 do print (xchr [vf [k]]); print ln(´ ␣´ ); count ← 0; for k ← 0 to 7 do

begin if eof (vf file) then vf abort (´ ␣ ␣ ␣ ´ ); read vf (temp byte); if temp byte = tfm[check sum + k] then incr (count ); end;

real dsize ← (((tfm[design size] ∗ 256 + tfm[design size + 1]) ∗ 256 + tfm[design size + 2]) ∗ 256 +

tfm[design size + 3])/´ 4000000 ; if count ≠ 8 then

begin print ln(´ ␣ ␣ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); end

This code is used in section 31.

33. ⟨ Read and store the font definitions and character packets 33 ⟩ ≡ for k ← 0 to 255 do packet start [k] ← vf size; font ptr ← 0; packet found ← false; font start [0] ← vf ptr ; repeat if eof (vf file) then

begin print ln(´ ␣ ␣ ␣ ␣ ´ ); temp byte ← post ; end

else begin read vf (temp byte); incr (vf count );

if temp byte ≠ post then if temp byte > long char then ⟨ Read and store a font definition 35 ⟩ else ⟨ Read and store a character packet 46 ⟩;

end; until temp byte = post

This code is used in section 31.

34. ⟨ Read and verify the postamble 34 ⟩ ≡

while (temp byte = post ) ∧ ¬eof (vf file) do begin read vf (temp byte); incr (vf count ); end;

if ¬eof (vf file) then

begin print ln(´  ´  ´ ␣    ␣     ␣    ␣  ␣   ␣   ␣  ␣   ␣  ␣  ´ );

  print ln(´ ´ ´ ␣       ␣  ␣  ␣  ␣     ´       ´ ␣  ´ );

end; if vf count mod 4 ≠ 0 then print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ )

This code is used in section 31.

35. ⟨ Read and store a font definition 35 ⟩ ≡

begin if packet found ∨ (temp byte ≥ pre) then

vf abort (´ ␣ ␣´ , temp byte : 1, ´ ␣ ␣ ␣ ␣ ␣ ´ ); font number [font ptr ] ← vf read (temp byte − fnt def1 + 1); if font ptr = max fonts then vf abort (´ ␣ ´ ´ ␣ ␣ ␣ ␣ ´ ); vf store(14); { c[4] s[4] d[4] a[1] l[1] } if vf [vf ptr − 10] > 0 then { s is negative or exceeds 2^{24} − 1 }

vf abort (´ ␣ ␣ ␣ ␣ ␣ ´ ); a ← vf [vf ptr − 2]; l ← vf [vf ptr − 1]; vf store(a + l); { n[a + l] } ⟨ Print the name of the local font 36 ⟩; ⟨ Read the local font’s file and record the characters it contains 39 ⟩; incr (font ptr ); font start [font ptr ] ← vf ptr ; end

This code is used in section 33.

36. The font area may need to be separated from the font name on some systems. Here we simply reproduce the font area and font name (with no space or punctuation between them). ⟨ Print the name of the local font 36 ⟩ ≡

print (´ ␣´ , font ptr : 1, ´ ␣´ ); for k ← font start [font ptr ] + 14 to vf ptr − 1 do print (xchr [vf [k]]); k ← font start [font ptr ] + 5; print ln(´ ␣ ␣´ , (((vf [k] ∗ 256 + vf [k + 1]) ∗ 256 + vf [k + 2])/´ 4000000 ) ∗ real dsize : 2 : 2, ´ ´ )

This code is used in section 35.

37. Now we must read in another file. But this time we needn’t be so careful, because we merely want to discover which characters are present. The next few sections of the program are copied pretty much verbatim from , so that system-dependent modifications can be copied from existing software.

It turns out to be convenient to read four bytes at a time, when we are inputting from the local files. The input goes into global variables b0 , b1 , b2 , and b3 , with b0 getting the first byte and b3 the fourth. ⟨ Globals in the outer block 7 ⟩ +≡ a: integer ; { length of the area/directory spec } l: integer ; { length of the font name proper } cur name: packed array [1 . . name length] of char ; { external name, with no lower case letters } b0 , b1 , b2 , b3 : byte; { four bytes input at once } font lh: 0 . . ´ 77777 ; { header length of current local font } font bc, font ec: 0 . . ´ 77777 ; { character range of current local font } 38. The read tfm word procedure sets b0 through b3 to the next four bytes in the current file.

define read tfm(#) ≡

if eof (tfm file) then # ← 0 else read (tfm file, #)

procedure read tfm word ; begin read tfm(b0 ); read tfm(b1 ); read tfm(b2 ); read tfm(b3 ); end;

39. We use the vf array to store a list of all valid characters in the local font, beginning at location font chars [f]. ⟨ Read the local font’s file and record the characters it contains 39 ⟩ ≡

font chars [font ptr ] ← vf ptr ; ⟨ Move font name into the cur name string 44 ⟩; reset (tfm file, cur name); if eof (tfm file) then print ln(´ ␣ ␣ ␣ ␣ ´ ´ ␣ ␣ ´ ) else begin font bc ← 0; font ec ← 256; { will cause error if not modified soon }

read tfm word ;

if b2 < 128 then begin font lh ← b2 ∗ 256 + b3 ; read tfm word ; if (b0 < 128) ∧ (b2 < 128) then

begin font bc ← b0 ∗ 256 + b1 ; font ec ← b2 ∗ 256 + b3 ; end; end;

if font bc ≤ font ec then if font ec > 255 then print ln(´ ␣ ␣ ␣ ␣ ´ ) else begin for k ← 0 to 3 + font lh do

begin read tfm word ;

if k = 4 then ⟨ Check the check sum 40 ⟩; if k = 5 then ⟨ Check the design size 41 ⟩; end;

for k ← font bc to font ec do begin read tfm word ; if b0 > 0 then { character k exists in the font }

begin vf [vf ptr ] ← k; incr (vf ptr );

if vf ptr = vf size then vf abort (´ ´ ´ ␣ ␣ ␣ ␣ ´ ); end; end; end;

if eof (tfm file) then print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); end;

incr (vf ptr ) { leave space for character search later }

This code is used in section 35.

40. ⟨ Check the check sum 40 ⟩ ≡

if b0 + b1 + b2 + b3 > 0 then

if (b0 ≠ vf [font start [font ptr ]]) ∨ (b1 ≠ vf [font start [font ptr ] + 1]) ∨

(b2 ≠ vf [font start [font ptr ] + 2]) ∨ (b3 ≠ vf [font start [font ptr ] + 3]) then begin print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); vf [font start [font ptr ]] ← b0 ; vf [font start [font ptr ] + 1] ← b1 ; vf [font start [font ptr ] + 2] ← b2 ; vf [font start [font ptr ] + 3] ← b3 ; end

This code is used in section 39.

41. ⟨ Check the design size 41 ⟩ ≡

if (b0 ≠ vf [font start [font ptr ] + 8]) ∨ (b1 ≠ vf [font start [font ptr ] + 9]) ∨

(b2 ≠ vf [font start [font ptr ] + 10]) ∨ (b3 ≠ vf [font start [font ptr ] + 11]) then begin print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); vf [font start [font ptr ] + 8] ← b0 ; vf [font start [font ptr ] + 9] ← b1 ; vf [font start [font ptr ] + 10] ← b2 ; vf [font start [font ptr ] + 11] ← b3 ; end

This code is used in section 39.

42. If no font directory has been specified, -reading software is supposed to use the default font directory, which is a system-dependent place where the standard fonts are kept. The string variable default directory contains the name of this area.

define default directory name ≡ ´ ´ { change this to the correct name } define default directory name length = 9 { change this to the correct length }

⟨ Globals in the outer block 7 ⟩ +≡ default directory : packed array [1 . . default directory name length] of char ;

43. ⟨ Set initial values 11 ⟩ +≡ default directory ← default directory name;

44. The string cur name is supposed to be set to the external name of the file for the current font. This usually means that we need to prepend the name of the default directory, and to append the suffix ‘ ’. Furthermore, we change lower case letters to upper case, since cur name is a Pascal string. ⟨ Move font name into the cur name string 44 ⟩ ≡

for k ← 1 to name length do cur name[k] ← ´ ␣´ ; if a = 0 then

begin for k ← 1 to default directory name length do cur name[k] ← default directory [k]; r ← default directory name length; end

else r ← 0; for k ← font start [font ptr ] + 14 to vf ptr − 1 do

begin incr (r); if r + 4 > name length then vf abort (´ ␣ ␣ ␣ ␣ ␣ ´ ); if (vf [k] ≥ ) ∧ (vf [k] ≤ ) then cur name[r] ← xchr [vf [k] − ´ 40 ] else cur name[r] ← xchr [vf [k]]; end;

cur name[r + 1] ← ´ ´ ; cur name[r + 2] ← ´ ´ ; cur name[r + 3] ← ´ ´ ; cur name[r + 4] ← ´ ´

This code is used in section 39.

45. It’s convenient to have a subroutine that reads a k-byte number from vf file.

define get vf (#) ≡

if eof (vf file) then # ← 0 else read vf (#)

function vf read (k : integer ): integer ; { actually 1 ≤ k ≤ 4 }

var b: byte; { input byte }

a: integer ; { accumulator } begin vf count ← vf count + k; get vf (b); a ← b; if k = 4 then

if b ≥ 128 then a ← a − 256; { 4-byte numbers are signed } while k > 1 do

begin get vf (b); a ← 256 ∗ a + b; decr (k);

end; vf read ← a; end;

46. The format supports arbitrary 4-byte character codes, but format presently does not. Therefore we give up if the character code is not between 0 and 255.

After more experience is gained with present-day files, the best way to extend them to arbitrary character codes will become clear; the extensions to and should not be difficult. ⟨ Read and store a character packet 46 ⟩ ≡

begin if temp byte = long char then begin pl ← vf read (4); c ← vf read (4); count ← vf read (4); { pl [4] cc[4] tfm[4] } end

else begin pl ← temp byte; c ← vf read (1); count ← vf read (3); { pl [1] cc[1] tfm[3] }

end; if nonexistent (c) then vf abort (´ ␣´ , c : 1, ´ ␣ ␣ ␣ ´ ); if packet start [c] < vf size then print ln(´ ␣ ␣ ␣ ␣ ␣´ , c : 1); if count ≠ tfm width(c) then print ln(´ ␣ ␣ ␣ ␣ ␣´ , c : 1, ´ ␣ ␣ ␣ ´ ); if pl < 0 then vf abort (´ ␣ ␣ ´ ); packet start [c] ← vf ptr ; vf store(pl ); packet end [c] ← vf ptr − 1; packet found ← true; end

This code is used in section 33.

47. The preceding code requires a simple subroutine that evaluates data. function tfm width(c : byte): integer ;

var a: integer ; { accumulator }

122  BASIC OUTPUT SUBROUTINES  VFtoVP §48

k: index ; { index into tfm } begin k ← width(c); { we assume that character c exists } a ← tfm[k]; if a ≥ 128 then a ← a − 256; tfm width ← ((256 ∗ a + tfm[k + 1]) ∗ 256 + tfm[k + 2]) ∗ 256 + tfm[k + 3]; end; 48. Basic output subroutines. Let us now define some procedures that will reduce the rest of ’s work to a triviality.

First of all, it is convenient to have an abbreviation for output to the file: define out (#) ≡ write(vpl file, #)

49. In order to stick to standard Pascal, we use an xchr array to do appropriate conversion of ASCII codes. Three other little strings are used to produce face codes like . ⟨ Globals in the outer block 7 ⟩ +≡ ASCII 04 , ASCII 10 , ASCII 14 : packed array [1 . . 32] of char ;

{ strings for output in the user’s external character set }

xchr : packed array [0 . . 255] of char ; MBL string , RI string , RCE string : packed array [1 . . 3] of char ;

{ handy string constants for face codes }

50. ⟨ Set initial values 11 ⟩ +≡ ASCII 04 ← ´ ␣ #$% ´ ´ ´ ; ASCII 10 ← ´ ´ ; ASCII 14 ← ´ ` ´ ; for k ← 0 to 255 do xchr [k] ← ´ ´ ; for k ← 0 to ´ 37 do

begin xchr [k + ´ 40 ] ← ASCII 04 [k + 1]; xchr [k + ´ 100 ] ← ASCII 10 [k + 1]; xchr [k + ´ 140 ] ← ASCII 14 [k + 1]; end;

MBL string ← ´  ´ ; RI string ← ´ ␣´ ; RCE string ← ´  ´ ;

51. The array dig will hold a sequence of digits to be output. ⟨ Globals in the outer block 7 ⟩ +≡ dig : array [0 . . 11] of 0 . . 9;

52. Here, in fact, are two procedures that output dig [j − 1] … dig [0], given j > 0. procedure out digs (j : integer ); { outputs j digits }

begin repeat decr (j); out (dig [j] : 1); until j = 0; end;

procedure print digs (j : integer ); { prints j digits } begin repeat decr (j); print (dig [j] : 1); until j = 0; end;

53. The print octal procedure indicates how print digs can be used. Since this procedure is used only to print character codes, it always produces three digits. procedure print octal (c : byte); { prints octal value of c }

var j: 0 . . 2; { index into dig } begin print (´ ´ ´ ´ ); { an apostrophe indicates the octal notation } for j ← 0 to 2 do

begin dig [j] ← c mod 8; c ← c div 8;

§54  VFtoVP  BASIC OUTPUT SUBROUTINES 123

end; print digs (3); end; 54. A file has nested parentheses, and we want to format the output so that its structure is clear. The level variable keeps track of the depth of nesting. ⟨ Globals in the outer block 7 ⟩ +≡ level : 0 . . 5;

55. ⟨ Set initial values 11 ⟩ +≡ level ← 0;

56. Three simple procedures suffice to produce the desired structure in the output. procedure out ln; { finishes one line, indents the next }

var l: 0 . . 5; begin write ln(vpl file); for l ← 1 to level do out (´ ␣␣␣´ ); end;

procedure left ; { outputs a left parenthesis } begin incr (level ); out (´ ´ ); end;

procedure right ; { outputs a right parenthesis and finishes a line } begin decr (level ); out (´ ´ ); out ln; end;

57. The value associated with a property can be output in a variety of ways. For example, we might want to output a BCPL string that begins in tfm[k]: procedure out BCPL(k : index ); { outputs a string, preceded by a blank space }

var l: 0 . . 39; { the number of bytes remaining } begin out (´ ␣´ ); l ← tfm[k]; while l > 0 do

begin incr (k); decr (l); out (xchr [tfm[k]]); end;

end;

58. The property value might also be a sequence of l bytes, beginning in tfm[k], that we would like to output in octal notation. The following procedure assumes that l ≤ 4, but larger values of l could be handled easily by enlarging the dig array and increasing the upper bounds on b and j. procedure out octal (k, l : index ); { outputs l bytes in octal }

var a: 0 . . ´ 1777 ; { accumulator for bits not yet output } b: 0 . . 32; { the number of significant bits in a } j: 0 . . 11; { the number of digits of output }

begin out (´ ␣ ␣´ ); { specify octal format } a ← 0; b ← 0; j ← 0; while l > 0 do ⟨ Reduce l by one, preserving the invariants 59 ⟩; while (a > 0) ∨ (j = 0) do

begin dig [j] ← a mod 8; a ← a div 8; incr (j); end;

out digs (j); end; 59. ⟨ Reduce l by one, preserving the invariants 59 ⟩ ≡

begin decr (l); if tfm[k + l] ≠ 0 then

begin while b > 2 do begin dig [j] ← a mod 8; a ← a div 8; b ← b − 3; incr (j); end;

case b of

0: a ← tfm[k + l]; 1: a ← a + 2 ∗ tfm[k + l]; 2: a ← a + 4 ∗ tfm[k + l]; end; end;

b ← b + 8; end

This code is used in section 58.

60. The property value may be a character, which is output in octal unless it is a letter or a digit. procedure out char (c : byte); { outputs a character }

begin if font type > vanilla then begin tfm[0] ← c; out octal (0, 1) end

else if ((c ≥ ) ∧ (c ≤ )) ∨ ((c ≥ ) ∧ (c ≤ )) ∨ ((c ≥ ) ∧ (c ≤ )) then

out (´ ␣ ␣´ , xchr [c]) else begin tfm[0] ← c; out octal (0, 1);

end; end;

61. The property value might be a “face” byte, which is output in the curious code mentioned earlier, provided that it is less than 18. procedure out face(k : index ); { outputs a face }

var s: 0 . . 1; { the slope }

b: 0 . . 8; { the weight and expansion } begin if tfm[k] ≥ 18 then out octal (k, 1) else begin out (´ ␣ ␣´ ); { specify face-code format }

s ← tfm[k] mod 2; b ← tfm[k] div 2; out (MBL string [1 + (b mod 3)]); out (RI string [1 + s]); out (RCE string [1 + (b div 3)]); end;

end; 62. And finally, the value might be a fix word , which is output in decimal notation with just enough decimal places for to recover every bit of the given fix word .

All of the numbers involved in the intermediate calculations of this procedure will be nonnegative and less than 10 · 2^{24}. procedure out fix (k : index ); { outputs a fix word }

var a: 0 . . ´ 7777 ; { accumulator for the integer part } f: integer ; { accumulator for the fraction part } j: 0 . . 12; { index into dig } delta: integer ; { amount if allowable inaccuracy }

begin out (´ ␣ ␣´ ); { specify real format } a ← (tfm[k] ∗ 16) + (tfm[k + 1] div 16); f ← ((tfm[k + 1] mod 16) ∗ ´ 400 + tfm[k + 2]) ∗ ´ 400 + tfm[k + 3]; if a > ´ 3777 then ⟨ Reduce negative to positive 65 ⟩; ⟨ Output the integer part, a, in decimal notation 63 ⟩; ⟨ Output the fraction part, f/2^{20}, in decimal notation 64 ⟩; end;

63. The following code outputs at least one digit even if a = 0. ⟨ Output the integer part, a, in decimal notation 63 ⟩ ≡

begin j ← 0; repeat dig [j] ← a mod 10; a ← a div 10; incr (j); until a = 0; out digs (j); end

This code is used in section 62.

64. And the following code outputs at least one digit to the right of the decimal point. ⟨ Output the fraction part, f/2^{20}, in decimal notation 64 ⟩ ≡

begin out (´ ´ ); f ← 10 ∗ f + 5; delta ← 10; repeat if delta > ´ 4000000 then f ← f + ´ 2000000 − (delta div 2);

out (f div ´ 4000000 : 1); f ← 10 ∗ (f mod ´ 4000000 ); delta ← delta ∗ 10; until f ≤ delta; end;

This code is used in section 62.

65. ⟨ Reduce negative to positive 65 ⟩ ≡ begin out (´ ´ ); a ← ´ 10000 − a; if f > 0 then

begin f ← ´ 4000000 − f; decr (a);

end; end

This code is used in section 62.

126  OUTPUTTING THE TFM INFO  VFtoVP §66

66. Outputting the TFM info. TEX checks the information of a file for validity as the file is being read in, so that no further checks will be needed when typesetting is going on. And when it finds something wrong, it justs calls the file “bad,” without identifying the nature of the problem, since files are supposed to be good almost all of the time.

Of course, a bad file shows up every now and again, and that’s where comes in. This program wants to catch at least as many errors as TEX does, and to give informative error messages besides. All of the errors are corrected, so that the output will be correct (unless, of course, the file was so loused up that no attempt is being made to fathom it).

67. Just before each character is processed, its code is printed in octal notation. Up to eight such codes appear on a line; so we have a variable to keep track of how many are currently there. We also keep track of whether or not any errors have had to be corrected. ⟨ Globals in the outer block 7 ⟩ +≡ chars on line: 0 . . 8; { the number of characters printed on the current line } perfect : boolean; { was the file free of errors? }

68. ⟨ Set initial values 11 ⟩ +≡ chars on line ← 0; perfect ← true; { innocent until proved guilty }

69. Error messages are given with the help of the bad and range error and bad char macros:

define bad (#) ≡ begin perfect ← false; if chars on line > 0 then print ln(´ ␣´ ); chars on line ← 0; print ln(´ ␣ ␣ ␣´ , #); end

define range error (#) ≡ begin perfect ← false; print ln(´ ␣´ ); print (#, ´ ␣ ␣ ␣ ␣´ ); print octal (c); print ln(´ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ␣ ␣ ´ ); end

define bad char tail (#) ≡ print octal (#); print ln(´ ´ );

end define bad char (#) ≡

begin perfect ← false;

if chars on line > 0 then print ln(´ ␣´ );

chars on line ← 0; print (´ ␣ ␣ ␣´ , #, ´ ␣ ␣ ␣´ ); bad char tail define correct bad char tail (#) ≡ print octal (tfm[#]); print ln(´ ´ ); tfm[#] ← bc;

end define correct bad char (#) ≡

begin perfect ← false;

if chars on line > 0 then print ln(´ ␣´ );

chars on line ← 0; print (´ ␣ ␣ ␣´ , #, ´ ␣ ␣ ␣´ ); correct bad char tail

§70  VFtoVP  OUTPUTTING THE TFM INFO 127

⟨ Globals in the outer block 7 ⟩ +≡ i: 0 . . ´ 77777 ; { an index to words of a subfile } c: 0 . . 256; { a random character } d: 0 . . 3; { byte number in a word } k: index ; { a random index } r: 0 . . 65535; { a random two-byte value } 70. There are a lot of simple things to do, and they have to be done one at a time, so we might as well get down to business. The first things that will put into the file appear in the header part. ⟨ Do the header 70 ⟩ ≡

begin font type ← vanilla; if lh ≥ 12 then

begin ⟨ Set the true font type 75 ⟩;

if lh ≥ 17 then begin ⟨ Output the family name 77 ⟩; if lh ≥ 18 then ⟨ Output the rest of the header 78 ⟩; end;

⟨ Output the character coding scheme 76 ⟩; end;

⟨ Output the design size 73 ⟩; ⟨ Output the check sum 71 ⟩; ⟨ Output the seven bit safe flag 79 ⟩; end

This code is used in section 132.

71. ⟨ Output the check sum 71 ⟩ ≡ left ; out (´ ´ ); out octal (check sum, 4); right

This code is used in section 70.

72. Incorrect design sizes are changed to 10 points.

define bad design(#) ≡ begin bad (´ ␣ ␣´ , #, ´ ´ ); print ln(´ ´ ´ ␣ ␣ ␣ ␣ ␣ ´ ); out (´ ␣ ␣ ´ ); end

73. ⟨ Output the design size 73 ⟩ ≡ left ; out (´ ´ ); if tfm[design size] > 127 then bad design(´ ´ ) else if (tfm[design size] = 0) ∧ (tfm[design size + 1] < 16) then bad design(´ ␣ ´ )

else out fix (design size); right ; out (´ ␣ ␣ ␣ ␣ ´ ); out ln; out (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); out ln

This code is used in section 70.

74. Since we have to check two different BCPL strings for validity, we might as well write a subroutine to make the check. procedure check BCPL(k, l : index ); { checks a string of length < l }

var j: index ; { runs through the string }

c: byte; { character being checked } begin if tfm[k] ≥ l then

begin bad (´ ␣ ␣ ␣ ␣ ´ ´ ␣ ␣ ␣ ´ ); tfm[k] ← 1; end;

for j ← k + 1 to k + tfm[k] do begin c ← tfm[j]; if (c = ) ∨ (c = ) then

begin bad (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); tfm[j] ← ; end

else if (c < ␣ ) ∨ (c > ) then begin bad (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); tfm[j] ← ; end

else if (c ≥ ) ∧ (c ≤ ) then tfm[j] ← c + − ; { upper-casify letters } end;

end;

75. The font type starts out vanilla; possibly we need to reset it. ⟨ Set the true font type 75 ⟩ ≡

begin check BCPL(scheme, 40); if (tfm[scheme] ≥ 11) ∧ (tfm[scheme + 1] = ) ∧ (tfm[scheme + 2] = ) ∧ (tfm[scheme + 3] = ) ∧

(tfm[scheme + 4] = ␣ ) ∧ (tfm[scheme + 5] = ) ∧ (tfm[scheme + 6] = ) ∧ (tfm[scheme + 7] = ) ∧ (tfm[scheme + 8] = ) ∧ (tfm[scheme + 9] = ␣ ) then

begin if (tfm[scheme + 10] = ) ∧ (tfm[scheme + 11] = ) then font type ← mathsy else if (tfm[scheme + 10] = ) ∧ (tfm[scheme + 11] = ) then font type ← mathex ; end;

end

This code is used in section 70.

76. ⟨ Output the character coding scheme 76 ⟩ ≡ left ; out (´ ´ ); out BCPL(scheme); right

This code is used in section 70.

77. ⟨ Output the family name 77 ⟩ ≡ left ; out (´ ´ ); check BCPL(family , 20); out BCPL(family ); right

This code is used in section 70.

78. ⟨ Output the rest of the header 78 ⟩ ≡ begin left ; out (´ ´ ); out face(random word + 3); right ; for i ← 18 to lh − 1 do

begin left ; out (´ ␣ ␣´ , i : 1); out octal (check sum + 4 ∗ i, 4); right ;

end; end

This code is used in section 70.

79. This program does not check to see if the seven bit safe flag has the correct setting, i.e., if it really reflects the seven-bit-safety of the file; the stated value is merely put into the file. The program will store a correct value and give a warning message if a file falsely claims to be safe. ⟨ Output the seven bit safe flag 79 ⟩ ≡

if (lh > 17) ∧ (tfm[random word ] > 127) then begin left ; out (´ ␣ ´ ); right ; end

This code is used in section 70.

80. The next thing to take care of is the list of parameters. ⟨ Do the parameters 80 ⟩ ≡

if np > 0 then begin left ; out (´ ´ ); out ln; for i ← 1 to np do ⟨ Check and output the ith parameter 82 ⟩; right ; end;

⟨ Check to see if np is complete for this font type 81 ⟩;

This code is used in section 132.

81. ⟨ Check to see if np is complete for this font type 81 ⟩ ≡

if (font type = mathsy ) ∧ (np ≠ 22) then

print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ , np : 1,

      ´ ␣   ␣  ´ )

else if (font type = mathex ) ∧ (np ≠ 13) then

print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ , np : 1,

        ´ ␣   ␣  ´ )

This code is used in section 80.

82. All fix word values except the design size and the first parameter will be checked to make sure that they are less than 16.0 in magnitude, using the check fix macro:

define check fix tail (#) ≡ bad (#, ´ ␣´ , i : 1, ´ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ␣ ␣ ´ );

end define check fix (#) ≡

if (tfm[#] > 0) ∧ (tfm[#] < 255) then

begin tfm[#] ← 0; tfm[(#) + 1] ← 0; tfm[(#) + 2] ← 0; tfm[(#) + 3] ← 0; check fix tail

⟨ Check and output the ith parameter 82 ⟩ ≡ begin left ; if i = 1 then out (´ ´ ) { this parameter is not checked } else begin check fix (param(i))(´ ´ );

⟨ Output the name of parameter i 83 ⟩;

end; out fix (param(i)); right ; end

This code is used in section 80.

83. ⟨ Output the name of parameter i 83 ⟩ ≡

if i ≤ 7 then

case i of
2: out (´  ´ ); 3: out (´  ´ ); 4: out (´  ´ );
5: out (´  ´ ); 6: out (´  ´ ); 7: out (´  ´ )

end else if (i ≤ 22) ∧ (font type = mathsy ) then

case i of
8: out (´  ´ ); 9: out (´  ´ ); 10: out (´  ´ );
11: out (´  ´ ); 12: out (´  ´ );
13: out (´  ´ ); 14: out (´  ´ ); 15: out (´  ´ );
16: out (´  ´ ); 17: out (´  ´ );
18: out (´  ´ ); 19: out (´  ´ );
20: out (´  ´ ); 21: out (´  ´ );

22: out (´ ´ )

end else if (i ≤ 13) ∧ (font type = mathex ) then

if i = 8 then out (´ ´ )

else out (´ ´ , i − 8 : 1) else out (´ ␣ ␣´ , i : 1)

This code is used in section 82.

84. We need to check the range of all the remaining fix word values, and to make sure that width[0] = 0, etc.

define nonzero fix (#) ≡ (tfm[#] > 0) ∨ (tfm[# + 1] > 0) ∨ (tfm[# + 2] > 0) ∨ (tfm[# + 3] > 0) ⟨ Check the fix word entries 84 ⟩ ≡

if nonzero fix (4 ∗ width base) then bad (´ ␣ ␣ ␣ ´ ); if nonzero fix (4 ∗ height base) then bad (´ ␣ ␣ ␣ ´ ); if nonzero fix (4 ∗ depth base) then bad (´ ␣ ␣ ␣ ´ ); if nonzero fix (4 ∗ italic base) then bad (´ ␣ ␣ ␣ ´ ); for i ← 0 to nw − 1 do check fix (4 ∗ (width base + i))(´ ´ ); for i ← 0 to nh − 1 do check fix (4 ∗ (height base + i))(´ ´ ); for i ← 0 to nd − 1 do check fix (4 ∗ (depth base + i))(´ ´ ); for i ← 0 to ni − 1 do check fix (4 ∗ (italic base + i))(´ ␣ ´ ); if nk > 0 then

for i ← 0 to nk − 1 do check fix (kern(i))(´ ´ );

This code is used in section 132.

85. The ligature/kerning program comes next. Before we can put it out in format, we need to make a table of “labels” that will be inserted into the program. For each character c whose tag is lig tag and whose starting address is r, we will store the pair (c, r) in the label table array. If there’s a boundary-char program starting at r, we also store the pair (256, r). This array is sorted by its second components, using the simple method of straight insertion. ⟨ Globals in the outer block 7 ⟩ +≡ label table: array [0 . . 258] of record

cc: 0 . . 256; rr : 0 . . lig size; end;

label ptr : 0 . . 257; { the largest entry in label table } sort ptr : 0 . . 257; { index into label table } boundary char : 0 . . 256; { boundary character, or 256 if none } bchar label : 0 . . ´ 77777 ; { beginning of boundary character program }

86. ⟨ Set initial values 11 ⟩ +≡ boundary char ← 256; bchar label ← ´ 77777 ; label ptr ← 0; label table[0].rr ← 0; { a sentinel appears at the bottom }

87. We’ll also identify and remove inaccessible program steps, using the activity array.

define unreachable = 0 { a program step not known to be reachable } define pass through = 1 { a program step passed through on initialization } define accessible = 2 { a program step that can be relevant }

⟨ Globals in the outer block 7 ⟩ +≡ activity : array [0 . . lig size] of unreachable . . accessible; ai , acti : 0 . . lig size; { indices into activity }

88. ⟨ Do the ligatures and kerns 88 ⟩ ≡

if nl > 0 then begin for ai ← 0 to nl − 1 do activity [ai ] ← unreachable; ⟨ Check for a boundary char 91 ⟩; end;

⟨ Build the label table 89 ⟩;

if nl > 0 then begin left ; out (´ ´ ); out ln; ⟨ Compute the activity array 92 ⟩; ⟨ Output and correct the ligature/kern program 93 ⟩; right ; ⟨ Check for ligature cycles 112 ⟩; end

This code is used in section 134.

89. We build the label table even when nl = 0, because this catches errors that would not otherwise be detected. ⟨ Build the label table 89 ⟩ ≡

for c ← bc to ec do

if tag (c) = lig tag then begin r ← remainder (c); if r < nl then

begin if tfm[lig step(r)] > stop flag then begin r ← 256 ∗ tfm[lig step(r) + 2] + tfm[lig step(r) + 3]; if r < nl then

if activity [remainder (c)] = unreachable then activity [remainder (c)] ← pass through; end; end;

if r ≥ nl then begin perfect ← false; print ln(´ ␣´ ); print (´ ␣ ␣ ␣ ␣ ␣´ ); print octal (c); print ln(´ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ´ ); reset tag (c); end

else ⟨ Insert (c, r) into label table 90 ⟩;

end; label table[label ptr + 1].rr ← lig size; { put “infinite” sentinel at the end }

This code is used in section 88.

90. ⟨ Insert (c, r) into label table 90 ⟩ ≡ begin sort ptr ← label ptr ; { there’s a hole at position sort ptr + 1 } while label table[sort ptr ].rr > r do

begin label table[sort ptr + 1] ← label table[sort ptr ]; decr (sort ptr ); { move the hole } end;

label table[sort ptr + 1].cc ← c; label table[sort ptr + 1].rr ← r; { fill the hole } incr (label ptr ); activity [r] ← accessible; end

This code is used in section 89.

91. ⟨ Check for a boundary char 91 ⟩ ≡

if tfm[lig step(0)] = 255 then begin left ; out (´ ´ ); boundary char ← tfm[lig step(0) + 1]; out char (boundary char ); right ; activity [0] ← pass through; end;

if tfm[lig step(nl − 1)] = 255 then begin r ← 256 ∗ tfm[lig step(nl − 1) + 2] + tfm[lig step(nl − 1) + 3]; if r ≥ nl then

begin perfect ← false; print ln(´ ␣´ );

print (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ´ ); end

else begin label ptr ← 1; label table[1].cc ← 256; label table[1].rr ← r; bchar label ← r; activity [r] ← accessible; end;

activity [nl − 1] ← pass through; end

This code is used in section 88.

92. ⟨ Compute the activity array 92 ⟩ ≡

for ai ← 0 to nl − 1 do

if activity [ai ] = accessible then begin r ← tfm[lig step(ai )]; if r < stop flag then

begin r ← r + ai + 1;

if r ≥ nl then begin bad (´ ␣ ␣´ , ai : 1, ´ ␣ ␣ ␣ ´ ); print ln(´ ␣ ␣ ␣ ´ ); tfm[lig step(ai )] ← stop flag ; end

else activity [r] ← accessible; end;

end

This code is used in section 88.

93. We ignore pass through items, which don’t need to be mentioned in the file. ⟨ Output and correct the ligature/kern program 93 ⟩ ≡

sort ptr ← 1; { point to the next label that will be needed } for acti ← 0 to nl − 1 do

if activity [acti ] ≠ pass through then begin i ← acti ; ⟨ Take care of commenting out unreachable steps 95 ⟩; ⟨ Output any labels for step i 94 ⟩; ⟨ Output step i of the ligature/kern program 96 ⟩; end;

if level = 2 then right { the final step was unreachable }

This code is used in section 88.

94. ⟨ Output any labels for step i 94 ⟩ ≡

while i = label table[sort ptr ].rr do begin left ; out (´ ´ ); if label table[sort ptr ].cc = 256 then out (´ ␣ ´ ) else out char (label table[sort ptr ].cc); right ; incr (sort ptr ); end

This code is used in section 93.

95. ⟨ Take care of commenting out unreachable steps 95 ⟩ ≡

if activity [i] = unreachable then

begin if level = 1 then begin left ; out (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); out ln; end

end else if level = 2 then right

This code is used in section 93.

96. ⟨ Output step i of the ligature/kern program 96 ⟩ ≡ begin k ← lig step(i); if tfm[k] > stop flag then

begin if 256 ∗ tfm[k + 2] + tfm[k + 3] ≥ nl then

    bad (´ ␣            ␣    ␣       ␣       ␣  ␣   ␣  ´ );

end else if tfm[k + 2] ≥ kern flag then ⟨ Output a kern step 98 ⟩

else ⟨ Output a ligature step 99 ⟩; if tfm[k] > 0 then

if level = 1 then ⟨ Output either or 97 ⟩; end

This code is used in sections 93 and 105.

97. The command is a bit tricky, because we will be omitting all inaccessible commands. ⟨ Output either or 97 ⟩ ≡

begin if tfm[k] ≥ stop flag then out (´ ´ ) else begin count ← 0;

for ai ← i + 1 to i + tfm[k] do

if activity [ai ] = accessible then incr (count ); out (´ ␣ ␣´ , count : 1, ´ ´ ); { possibly count = 0, so who cares } end;

out ln; end

This code is used in section 96.

98. ⟨ Output a kern step 98 ⟩ ≡

begin if nonexistent (tfm[k + 1]) then

if tfm[k + 1] ≠ boundary char then correct bad char (´ ␣ ␣ ´ )(k + 1); left ; out (´ ´ ); out char (tfm[k + 1]); r ← 256 ∗ (tfm[k + 2] − kern flag ) + tfm[k + 3]; if r ≥ nk then

begin bad (´ ␣ ␣ ␣ ´ ); out (´ ␣ ␣ ´ );

end else out fix (kern(r)); right ; end

This code is used in section 96.

99. ⟨ Output a ligature step 99 ⟩ ≡

begin if nonexistent (tfm[k + 1]) then

if tfm[k + 1] ≠ boundary char then correct bad char (´ ␣ ␣ ´ )(k + 1); if nonexistent (tfm[k + 3]) then correct bad char (´ ␣ ␣ ␣ ´ )(k + 3); left ; r ← tfm[k + 2]; if (r = 4) ∨ ((r > 7) ∧ (r ≠ 11)) then

begin print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); r ← 0; tfm[k + 2] ← 0; end;

if r mod 4 > 1 then out (´ ´ ); out (´ ´ ); if odd (r) then out (´ ´ ); while r > 3 do

begin out (´ ´ ); r ← r − 4; end;

out char (tfm[k + 1]); out char (tfm[k + 3]); right ; end

This code is used in section 96.

100. The last thing on ’s agenda is to go through the list of char info and spew out the information about each individual character. ⟨ Do the characters 100 ⟩ ≡

sort ptr ← 0; { this will suppress ‘ ’ lines in ligature comments } for c ← bc to ec do

if width index (c) > 0 then

begin if chars on line = 8 then begin print ln(´ ␣´ ); chars on line ← 1; end

else begin if chars on line > 0 then print (´ ␣´ ); incr (chars on line); end;

print octal (c); { progress report } left ; out (´ ´ ); out char (c); out ln; ⟨ Output the character’s width 101 ⟩; if height index (c) > 0 then ⟨ Output the character’s height 102 ⟩; if depth index (c) > 0 then ⟨ Output the character’s depth 103 ⟩; if italic index (c) > 0 then ⟨ Output the italic correction 104 ⟩; case tag (c) of no tag : do nothing ; lig tag : ⟨ Output the applicable part of the ligature/kern program as a comment 105 ⟩; list tag : ⟨ Output the character link unless there is a problem 106 ⟩; ext tag : ⟨ Output an extensible character recipe 107 ⟩; end; if ¬do map(c) then goto final end ; right ; end

This code is used in section 133.

101. ⟨ Output the character’s width 101 ⟩ ≡ begin left ; out (´ ´ ); if width index (c) ≥ nw then range error (´ ´ ) else out fix (width(c)); right ; end

This code is used in section 100.

102. ⟨ Output the character’s height 102 ⟩ ≡ if height index (c) ≥ nh then range error (´ ´ ) else begin left ; out (´ ´ ); out fix (height (c)); right ;

end

This code is used in section 100.

103. ⟨ Output the character’s depth 103 ⟩ ≡ if depth index (c) ≥ nd then range error (´ ´ ) else begin left ; out (´ ´ ); out fix (depth(c)); right ;

end

This code is used in section 100.

104. ⟨ Output the italic correction 104 ⟩ ≡ if italic index (c) ≥ ni then range error (´ ␣ ´ ) else begin left ; out (´ ´ ); out fix (italic(c)); right ;

end

This code is used in section 100.

105. ⟨ Output the applicable part of the ligature/kern program as a comment 105 ⟩ ≡ begin left ; out (´ ´ ); out ln; i ← remainder (c); r ← lig step(i); if tfm[r] > stop flag then i ← 256 ∗ tfm[r + 2] + tfm[r + 3]; repeat ⟨ Output step i of the ligature/kern program 96 ⟩;

if tfm[k] ≥ stop flag then i ← nl else i ← i + 1 + tfm[k];

until i ≥ nl ; right ; end

This code is used in section 100.

106. We want to make sure that there is no cycle of characters linked together by list tag entries, since such a cycle would get TEX into an endless loop. If such a cycle exists, the routine here detects it when processing the largest character code in the cycle. ⟨ Output the character link unless there is a problem 106 ⟩ ≡

begin r ← remainder (c); if nonexistent (r) then

begin bad char (´ ␣ ␣ ␣ ´ )(r); reset tag (c); end

else begin while (r < c) ∧ (tag (r) = list tag ) do r ← remainder (r);

if r = c then begin bad (´ ␣ ␣ ␣ ␣ ´ ); print (´ ␣´ ); print octal (c); print ln(´ ␣ ␣ ␣ ␣ ´ ); reset tag (c); end

else begin left ; out (´ ´ ); out char (remainder (c)); right ;

end; end;

end

This code is used in section 100.

107. ⟨ Output an extensible character recipe 107 ⟩ ≡

if remainder (c) ≥ ne then begin range error (´ ´ ); reset tag (c); end

else begin left ; out (´ ´ ); out ln; ⟨ Output the extensible pieces that exist 108 ⟩; right ; end

This code is used in section 100.

108. ⟨ Output the extensible pieces that exist 108 ⟩ ≡

for k ← 0 to 3 do

if (k = 3) ∨ (tfm[exten(c) + k] > 0) then begin left ;

case k of

0: out (´ ´ ); 1: out (´ ´ ); 2: out (´ ´ ); 3: out (´ ´ ) end; if nonexistent (tfm[exten(c) + k]) then out char (c) else out char (tfm[exten(c) + k]); right ; end

This code is used in section 107.

109. Some of the extensible recipes may not actually be used, but TEX will complain about them anyway if they refer to nonexistent characters. Therefore must check them too. ⟨ Check the extensible recipes 109 ⟩ ≡

if ne > 0 then

for c ← 0 to ne − 1 do for d ← 0 to 3 do

begin k ← 4 ∗ (exten base + c) + d; if (tfm[k] > 0) ∨ (d = 3) then

begin if nonexistent (tfm[k]) then begin bad char (´ ␣ ␣ ␣ ´ )(tfm[k]); if d < 3 then tfm[k] ← 0; end; end; end

This code is used in section 134.

§110  VFtoVP  CHECKING FOR LIGATURE LOOPS 139

110. Checking for ligature loops. We have programmed almost everything but the most interesting calculation of all, which has been saved for last as a special treat. TEX’s extended ligature mechanism allows unwary users to specify sequences of ligature replacements that never terminate. For example, the pair of commands

x y y x

alternately replaces character x by character y and vice versa. A similar loop occurs if z y occurs in the program for x and z x occurs in the program for y.

More complicated loops are also possible. For example, suppose the ligature programs for x and y are

\begin{matrix}x  z w  w y …, \\ y  w x …; \end{matrix}

then the adjacent characters xz change to xwz, xywz, xxz, xxwz, …, ad infinitum.

111. To detect such loops, attempts to evaluate the function f(x, y) for all character pairs x and y, where f is defined as follows: If the current character is x and the next character is y, we say the “cursor” is between x and y; when the cursor first moves past y, the character immediately to its left is f(x, y). This function is defined if and only if no infinite loop is generated when the cursor is between x and y.

The function f(x, y) can be defined recursively. It turns out that all pairs (x, y) belong to one of five classes. The simplest class has f(x, y) = y; this happens if there’s no ligature between x and y, or in the cases and . Another simple class arises when there’s a or between x and y, generating the character z; then f(x, y) = z. Otherwise we always have f(x, y) equal to either f(x, z) or f(z, y) or f(f(x, z), y), where z is the inserted ligature character.

The first two of these classes can be merged; we can also consider (x, y) to belong to the simple class when f(x, y) has been evaluated. For technical reasons we allow x to be 256 (for the boundary character at the left) or 257 (in cases when an error has been detected).

For each pair (x, y) having a ligature program step, we store (x, y) in a hash table from which the values z and class can be read.

define simple = 0 { f(x, y) = z }

define left z = 1 { f(x, y) = f(z, y) }

define right z = 2 { f(x, y) = f(x, z) }

define both z = 3 { f(x, y) = f(f(x, z), y) }

140  CHECKING FOR LIGATURE LOOPS  VFtoVP §112

define pending = 4 { f(x, y) is being evaluated } ⟨ Globals in the outer block 7 ⟩ +≡ hash: array [0 . . hash size] of 0 . . 66048; { 256x + y + 1 for x ≤ 257 and y ≤ 255 } class : array [0 . . hash size] of simple . . pending ; lig z : array [0 . . hash size] of 0 . . 257; hash ptr : 0 . . hash size; { the number of nonzero entries in hash } hash list : array [0 . . hash size] of 0 . . hash size; { list of those nonzero entries } h, hh: 0 . . hash size; { indices into the hash table } x lig cycle, y lig cycle: 0 . . 256; { problematic ligature pair } 112. ⟨ Check for ligature cycles 112 ⟩ ≡

hash ptr ← 0; y lig cycle ← 256; for hh ← 0 to hash size do hash[hh] ← 0; { clear the hash table } for c ← bc to ec do

if tag (c) = lig tag then begin i ← remainder (c); if tfm[lig step(i)] > stop flag then i ← 256 ∗ tfm[lig step(i) + 2] + tfm[lig step(i) + 3]; ⟨ Enter data for character c starting at location i in the hash table 113 ⟩; end;

if bchar label < nl then begin c ← 256; i ← bchar label ; ⟨ Enter data for character c starting at location i in the hash table 113 ⟩; end;

if hash ptr = hash size then begin print ln(´ ␣ ␣ ´ ´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); goto final end ; end;

for hh ← 1 to hash ptr do begin r ← hash list [hh]; if class [r] > simple then { make sure f is defined }

r ← f(r, (hash[r] − 1) div 256, (hash[r] − 1) mod 256); end;

if y lig cycle < 256 then begin print (´ ␣ ␣ ␣ ␣ ␣´ ); if x lig cycle = 256 then print (´ ´ ) else print octal (x lig cycle); print (´ ␣ ␣´ ); print octal (y lig cycle); print ln(´ ´ ); out (´ ␣ ␣ ␣ ␣ ␣ ´ ); goto final end ; end

This code is used in section 88.

113. ⟨ Enter data for character c starting at location i in the hash table 113 ⟩ ≡

repeat hash input ; k ← tfm[lig step(i)]; if k ≥ stop flag then i ← nl else i ← i + 1 + k;

until i ≥ nl

This code is used in sections 112 and 112.

114. We use an “ordered hash table” with linear probing, because such a table is efficient when the lookup of a random key tends to be unsuccessful. procedure hash input ; { enter data for character c and command i }

label exit ; var cc: simple . . both z ; { class of data being entered }

zz : 0 . . 255; { function value or ligature character being entered } y: 0 . . 255; { the character after the cursor } key : integer ; { value to be stored in hash } t: integer ; { temporary register for swapping }

begin if hash ptr = hash size then return; ⟨ Compute the command parameters y, cc, and zz 115 ⟩; key ← 256 ∗ c + y + 1; h ← (1009 ∗ key ) mod hash size; while hash[h] > 0 do

begin if hash[h] ≤ key then begin if hash[h] = key then return; { unused ligature command } t ← hash[h]; hash[h] ← key ; key ← t; { do ordered-hash-table insertion } t ← class [h]; class [h] ← cc; cc ← t; { namely, do a swap } t ← lig z [h]; lig z [h] ← zz ; zz ← t; end;

if h > 0 then decr (h) else h ← hash size;

end; hash[h] ← key ; class [h] ← cc; lig z [h] ← zz ; incr (hash ptr ); hash list [hash ptr ] ← h;

exit : end;

115. We must store kern commands as well as ligature commands, because the former might make the latter inapplicable. ⟨ Compute the command parameters y, cc, and zz 115 ⟩ ≡

k ← lig step(i); y ← tfm[k + 1]; t ← tfm[k + 2]; cc ← simple; zz ← tfm[k + 3]; if t ≥ kern flag then zz ← y

else begin case t of

0, 6: do nothing ; { , }

5, 11: zz ← y; {  ,  }
1, 7: cc ← left z ; {  ,  }

2: cc ← right z ; { }

3: cc ← both z ; { }

end; { there are no other cases }

end

This code is used in section 114.

116. Evaluation of f(x, y) is handled by two mutually recursive procedures. Kind of a neat algorithm, generalizing a depth-first search. function f(h, x, y : index ): index ; forward ; { compute f for arguments known to be in hash[h] } function eval (x, y : index ): index ; { compute f(x, y) with hashtable lookup }

var key : integer ; { value sought in hash table } begin key ← 256 ∗ x + y + 1; h ← (1009 ∗ key ) mod hash size; while hash[h] > key do

if h > 0 then decr (h) else h ← hash size; if hash[h] < key then eval ← y { not in ordered hash table } else eval ← f(h, x, y); end; 117. Pascal’s beastly convention for forward declarations prevents us from saying function f(h, x, y : index ): index here. function f;

begin case class [h] of simple: do nothing ; left z : begin class [h] ← pending ; lig z [h] ← eval (lig z [h], y); class [h] ← simple;

end; right z : begin class [h] ← pending ; lig z [h] ← eval (x, lig z [h]); class [h] ← simple;

end; both z : begin class [h] ← pending ; lig z [h] ← eval (eval (x, lig z [h]), y); class [h] ← simple;

end; pending : begin x lig cycle ← x; y lig cycle ← y; lig z [h] ← 257; class [h] ← simple;

§118  VFtoVP  OUTPUTTING THE VF INFO 143

end; { the value 257 will break all cycles, since it’s not in hash } end; { there are no other cases } f ← lig z [h]; end; 118. Outputting the VF info. The routines we’ve used for output from the tfm array have counterparts for output from vf . One difference is that the string outputs from vf need to be checked for balanced parentheses. The string balance routine tests the string of length l that starts at location k. function string balance(k, l : integer ): boolean;

label not found , exit ; var j, bal : integer ; begin if l > 0 then

if vf [k] = ␣ then goto not found ; { a leading blank is considered unbalanced } bal ← 0; for j ← k to k + l − 1 do

begin if (vf [j] < ␣ ) ∨ (vf [j] ≥ 127) then goto not found ;

if vf [j] = then incr (bal )

else if vf [j] = then if bal = 0 then goto not found else decr (bal );

end; if bal > 0 then goto not found ; string balance ← true; return;

not found : string balance ← false; exit : end;

119. define bad vf (#) ≡ begin perfect ← false; if chars on line > 0 then print ln(´ ␣´ ); chars on line ← 0; print ln(´ ␣ ␣ ␣´ , #); end

⟨ Do the virtual font title 119 ⟩ ≡

if string balance(0, font start [0]) then begin left ; out (´ ␣´ ); for k ← 0 to font start [0] − 1 do out (xchr [vf [k]]); right ; end

else bad vf (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ )

This code is used in section 132.

120. We can re-use some code by moving fix word data to tfm, using the fact that the design size has already been output. procedure out as fix (x : integer );

var k: 1 . . 3; begin if abs (x) ≥ ´ 100000000 then bad vf (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); if x ≥ 0 then tfm[design size] ← 0 else begin tfm[design size] ← 255; x ← x + ´ 100000000 ;

end; for k ← 3 downto 1 do

begin tfm[design size + k] ← x mod 256; x ← x div 256;

144  OUTPUTTING THE VF INFO  VFtoVP §121

end; out fix (design size); end; 121. ⟨ Do the local fonts 121 ⟩ ≡

for f ← 0 to font ptr − 1 do begin left ; out (´ ␣ ␣´ , f : 1); out ln; ⟨ Output the font area and name 122 ⟩; for k ← 0 to 11 do tfm[k] ← vf [font start [f] + k]; if tfm[0] + tfm[1] + tfm[2] + tfm[3] > 0 then

begin left ; out (´ ´ ); out octal (0, 4); right ;

end; left ; out (´ ´ ); out fix (4); right ; left ; out (´ ´ ); out fix (8); right ; right ; end

This code is used in section 132.

122. ⟨ Output the font area and name 122 ⟩ ≡ a ← vf [font start [f] + 12]; l ← vf [font start [f] + 13]; if a > 0 then

if ¬string balance(font start [f] + 14, a) then bad vf (´ ␣ ␣ ␣ ␣ ␣ ´ )

else begin left ; out (´ ␣´ ); for k ← font start [f] + 14 to font start [f] + a + 13 do out (xchr [vf [k]]); right ; end;

if (l = 0) ∨ ¬string balance(font start [f] + 14 + a, l) then

bad vf (´ ␣ ␣ ␣ ␣ ␣ ´ ) else begin left ; out (´ ␣´ );

for k ← font start [f] + 14 + a to font start [f] + a + l + 13 do out (xchr [vf [k]]);

right ; end

This code is used in section 121.

123. Now we get to the interesting part of output, where commands are translated into symbolic form. The language is a subset of , so we sometimes need to output semantic equivalents of the commands instead of producing a literal translation. This causes a small but tolerable loss of efficiency. We need to simulate the stack used by -reading software. ⟨ Globals in the outer block 7 ⟩ +≡ top: 0 . . max stack ; { stack pointer } wstack , xstack , ystack , zstack : array [0 . . max stack ] of integer ;

{ stacked values of registers w, x, y, z }

vf limit : 0 . . vf size; { the current packet ends here } o: byte; { the current opcode } 124. ⟨ Do the packet for character c 124 ⟩ ≡

if packet start [c] = vf size then bad vf (´ ␣ ␣ ␣ ␣´ , c : 1) else begin left ; out (´ ´ ); out ln; top ← 0; wstack [0] ← 0; xstack [0] ← 0; ystack [0] ← 0;

zstack [0] ← 0; vf ptr ← packet start [c]; vf limit ← packet end [c] + 1; f ← 0; while vf ptr < vf limit do

begin o ← vf [vf ptr ]; incr (vf ptr );

case o of

⟨ Cases of instructions that can appear in character packets 126 ⟩ improper DVI for VF : bad vf (´ ␣ ␣ ␣´ , o : 1, ´ ␣ ␣ ␣ ´ ); end; { there are no other cases } end;

if top > 0 then begin bad vf (´ ␣ ␣ ␣ ´ ); repeat out (´ ´ ); decr (top); until top = 0; end;

right ;

end

This code is used in section 133.

125. A procedure called get bytes helps fetch the parameters of commands. function get bytes (k : integer ; signed : boolean): integer ;

var a: integer ; { accumulator } begin if vf ptr + k > vf limit then

begin bad vf (´ ␣ ␣ ´ ); k ← vf limit − vf ptr ; end;

a ← vf [vf ptr ]; if (k = 4) ∨ signed then

if a ≥ 128 then a ← a − 256; incr (vf ptr ); while k > 1 do

begin a ← a ∗ 256 + vf [vf ptr ]; incr (vf ptr ); decr (k);

end; get bytes ← a; end; 126. Let’s look at the simplest cases first, in order to get some experience.

define four cases (#) ≡ #, # + 1, # + 2, # + 3 define eight cases (#) ≡ four cases (#), four cases (# + 4) define sixteen cases (#) ≡ eight cases (#), eight cases (# + 8) define thirty two cases (#) ≡ sixteen cases (#), sixteen cases (# + 16) define sixty four cases (#) ≡ thirty two cases (#), thirty two cases (# + 32)

⟨ Cases of instructions that can appear in character packets 126 ⟩ ≡ nop: do nothing ; push: begin if top = max stack then

begin print ln(´ ␣ ´ ); goto final end ; end;

incr (top); wstack [top] ← wstack [top − 1]; xstack [top] ← xstack [top − 1]; ystack [top] ← ystack [top − 1]; zstack [top] ← zstack [top − 1]; out (´ ´ ); out ln; end;

pop: if top = 0 then bad vf (´ ␣ ␣ ␣ ´ )

else begin decr (top); out (´ ´ ); out ln;

end; set rule, put rule: begin if o = put rule then out (´ ´ );

left ; out (´ ´ ); out as fix (get bytes (4, true)); out as fix (get bytes (4, true)); if o = put rule then out (´ ´ ); right ; end;

See also sections 127, 128, 129, and 130. This code is used in section 124.

127. Horizontal and vertical motions become  and  in  lingo.

⟨ Cases of instructions that can appear in character packets 126 ⟩ +≡ four cases (right1 ): begin out (´ ´ ); out as fix (get bytes (o − right1 + 1, true)); out (´ ´ );

out ln; end; w0 , four cases (w1 ): begin if o ≠ w0 then wstack [top] ← get bytes (o − w1 + 1, true);

out (´ ´ ); out as fix (wstack [top]); out (´ ´ ); out ln; end; x0 , four cases (x1 ): begin if o ≠ x0 then xstack [top] ← get bytes (o − x1 + 1, true);

out (´ ´ ); out as fix (xstack [top]); out (´ ´ ); out ln; end; four cases (down1 ): begin out (´ ´ ); out as fix (get bytes (o − down1 + 1, true)); out (´ ´ );

out ln; end; y0 , four cases (y1 ): begin if o ≠ y0 then ystack [top] ← get bytes (o − y1 + 1, true);

out (´ ´ ); out as fix (ystack [top]); out (´ ´ ); out ln; end; z0 , four cases (z1 ): begin if o ≠ z0 then zstack [top] ← get bytes (o − z1 + 1, true);

out (´ ´ ); out as fix (zstack [top]); out (´ ´ ); out ln; end;

128. Variable f always refers to the current font. If f = font ptr , it’s a font that hasn’t been defined (so its characters will be ignored). ⟨ Cases of instructions that can appear in character packets 126 ⟩ +≡ sixty four cases (fnt num 0 ), four cases (fnt1 ): begin f ← 0;

if o ≥ fnt1 then font number [font ptr ] ← get bytes (o − fnt1 + 1, false) else font number [font ptr ] ← o − fnt num 0 ; while font number [f] ≠ font number [font ptr ] do incr (f); if f = font ptr then bad vf (´ ␣ ␣ ´ ) else begin out (´ ␣ ␣´ , f : 1, ´ ´ ); out ln;

end; end; 129. Before we typeset a character we make sure that it exists. ⟨ Cases of instructions that can appear in character packets 126 ⟩ +≡ sixty four cases (set char 0 ), sixty four cases (set char 0 + 64), four cases (set1 ), four cases (put1 ): begin if

o ≥ set1 then if o ≥ put1 then c ← get bytes (o − put1 + 1, false) else c ← get bytes (o − set1 + 1, false)

else c ← o; if f = font ptr then bad vf (´ ␣´ , c : 1, ´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ) else begin vf [font start [f + 1] − 1] ← c; { store c in the “hole” we left }

k ← font chars [f]; while vf [k] ≠ c do incr (k); if k = font start [f + 1] − 1 then bad vf (´ ␣´ , c : 1, ´ ␣ ␣ ␣´ , f : 1, ´ ␣ ␣ ␣ ´ ) else begin if o ≥ put1 then out (´ ´ );

left ; out (´ ´ ); out char (c);

if o ≥ put1 then out (´ ´ );

right ;

end; end;

end;

130. The “special” commands are the only ones remaining to be dealt with. We use a hexadecimal output in the general case, if a simple string would be inadequate.

define out hex (#) ≡ begin a ← #; if a < 10 then out (a : 1) else out (xchr [a − 10 + ]); end

⟨ Cases of instructions that can appear in character packets 126 ⟩ +≡ four cases (xxx1 ): begin k ← get bytes (o − xxx1 + 1, false);

if k < 0 then bad vf (´ ␣ ␣ ␣ ´ ) else begin left ;

if k + vf ptr > vf limit then begin bad vf (´ ␣ ␣ ␣ ␣ ␣ ´ ); k ← vf limit − vf ptr ; end;

if (k > 64) ∨ ¬string balance(vf ptr , k) then begin out (´ ␣´ ); while k > 0 do

begin if k mod 32 = 0 then out ln else if k mod 4 = 0 then out (´ ␣´ ); out hex (vf [vf ptr ] div 16); out hex (vf [vf ptr ] mod 16); incr (vf ptr ); decr (k); end;

end else begin out (´ ␣´ );

while k > 0 do begin out (xchr [vf [vf ptr ]]); incr (vf ptr ); decr (k); end;

end; right ; end;

end;

148  THE MAIN PROGRAM  VFtoVP §131

131. The main program. The routines sketched out so far need to be packaged into separate procedures, on some systems, since some Pascal compilers place a strict limit on the size of a routine. The packaging is done here in an attempt to avoid some system-dependent changes.

First come the vf input and organize procedures, which read the input data and get ready for subsequent events. If something goes wrong, the routines return false. function vf input : boolean;

label final end , exit ; var vf ptr : 0 . . vf size; { an index into vf }

k: integer ; { all-purpose index } c: integer ; { character code }

begin ⟨ Read the whole file 31 ⟩; vf input ← true; return;

final end : vf input ← false; exit : end; function organize: boolean;

label final end , exit ; var tfm ptr : index ; { an index into tfm } begin ⟨ Read the whole file 24 ⟩; ⟨ Set subfile sizes lh, bc, …, np 25 ⟩; ⟨ Compute the base addresses 27 ⟩; organize ← vf input ; return;

final end : organize ← false; exit : end;

132. Next we do the simple things. procedure do simple things ;

var i: 0 . . ´ 77777 ; { an index to words of a subfile } f: 0 . . vf size; { local font number } k: integer ; { all-purpose index }

begin ⟨ Do the virtual font title 119 ⟩; ⟨ Do the header 70 ⟩; ⟨ Do the parameters 80 ⟩; ⟨ Do the local fonts 121 ⟩; ⟨ Check the fix word entries 84 ⟩; end;

§133  VFtoVP  THE MAIN PROGRAM 149

133. And then there’s a routine for individual characters. function do map(c : byte): boolean;

label final end , exit ; var k: integer ; f: 0 . . vf size; { current font number } begin ⟨ Do the packet for character c 124 ⟩; do map ← true; return;

final end : do map ← false; exit : end; function do characters : boolean;

label final end , exit ; var c: byte; { character being done }

k: index ; { a random index }

ai : 0 . . lig size; { index into activity } begin ⟨ Do the characters 100 ⟩; do characters ← true; return;

final end : do characters ← false; exit : end;

134. Here is where begins and ends.

begin initialize; if ¬organize then goto final end ; do simple things ; ⟨ Do the ligatures and kerns 88 ⟩; ⟨ Check the extensible recipes 109 ⟩; if ¬do characters then goto final end ; print ln(´ ´ ); if level ≠ 0 then print ln(´ ␣ ␣ ´ ´ ␣ ´ ); if ¬perfect then

begin out (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣´ ); out (´ ␣ ␣ ␣ ␣ ␣ ´ ); end;

final end : end.

150  SYSTEM-DEPENDENT CHANGES  VFtoVP §135

135. System-dependent changes. This section should be replaced, if necessary, by changes to the program that are necessary to make work at a particular installation. It is usually best to design your change file so that all changes to previous sections preserve the section numbering; then everybody’s version will be consistent with the printed program. More extensive changes, which introduce new sections, can be inserted here; then only the index itself will get a new section number.

§136  VFtoVP  INDEX 151

136. Index. Pointers to error messages appear here together with the section numbers where each identifier is used.

a: \underline{37}, \underline{45}, \underline{47}, \underline{58}, \underline{62}, \underline{125}. abort : \underline{24}, 25. abs : 120. accessible: \underline{87}, 90, 91, 92, 97. acti : \underline{87}, 93. activity : \underline{87}, 88, 89, 90, 91, 92, 93, 95, 97, 133. ai : \underline{87}, 88, 92, 97, \underline{133}. ASCII 04 : \underline{49}, 50. ASCII 10 : \underline{49}, 50. ASCII 14 : \underline{49}, 50. axis height : 19. b: \underline{45}, \underline{58}, \underline{61}. bad : \underline{69}, 72, 74, 82, 84, 92, 96, 98, 106. : 69. : 39. : 119. bad char : \underline{69}, 106, 109. bad char tail : \underline{69}. bad design: \underline{72}, 73. bad vf : \underline{119}, 120, 122, 124, 125, 126, 128, 129, 130. bal : \underline{118}. banner : \underline{1}, 2. bc: \underline{12}, 13, 15, 17, 25, 27, 28, 69, 89, 100, 112. bchar label : \underline{85}, 86, 91, 112. big op spacing1 : 19. big op spacing5 : 19. boolean: 30, 67, 118, 125, 131, 133. bop: 8. bot : 18. both z : \underline{111}, 114, 115, 117. boundary char : \underline{85}, 86, 91, 98, 99. byte: \underline{5}, 7, 10, 23, 30, 37, 45, 47, 53, 60, 74,

123, 133. b0 : \underline{37}, 38, 39, 40, 41. b1 : \underline{37}, 38, 39, 40, 41. b2 : \underline{37}, 38, 39, 40, 41. b3 : \underline{37}, 38, 39, 40, 41. c: \underline{47}, \underline{60}, \underline{69}, \underline{74}, \underline{131}, \underline{133}. cc: 8, 46, \underline{85}, 90, 91, 94, \underline{114}, 115. char : 37, 42, 49. char base: \underline{26}, 27, 28. char info: 15, 26, \underline{28}, 100. char info word : 13, 15, 16. : 46. : 106. : 129. chars on line: \underline{67}, 68, 69, 100, 119. check sum: 14. : 32. check BCPL: \underline{74}, 75, 77. check fix : \underline{82}, 84. check fix tail : \underline{82}. check sum: \underline{28}, 32, 71, 78. class : \underline{111}, 112, 114, 117. coding scheme: 14. correct bad char : \underline{69}, 98, 99. correct bad char tail : \underline{69}. count : \underline{30}, 32, 46, 97. cs : 7. cur name: \underline{37}, 39, 44. : 106. d: \underline{69}. decr : \underline{5}, 45, 52, 56, 57, 59, 65, 90, 114, 116, 118,

152  INDEX  VFtoVP §136

124, 125, 126, 130. default directory : \underline{42}, 43, 44. default directory name: \underline{42}, 43. default directory name length: \underline{42}, 44. default rule thickness : 19. delim1 : 19. delim2 : 19. delta: \underline{62}, 64. denom1 : 19. denom2 : 19. depth: 15, \underline{28}, 103. : 103. : 84. depth base: \underline{26}, 27, 28, 84. depth index : 15, \underline{28}, 100, 103. design size: 14. : 72. : 41. design size: \underline{28}, 32, 73, 120. : 73. dig : \underline{51}, 52, 53, 58, 59, 62, 63. : 46. do characters : \underline{133}, 134. do map: 100, \underline{133}. do nothing : \underline{5}, 100, 115, 117, 126. do simple things : \underline{132}, 134. down1 : \underline{8}, 127. ds : 7. dvi : 8. ec: \underline{12}, 13, 15, 17, 25, 27, 28, 89, 100, 112. eight cases : \underline{126}. eof : 24, 32, 33, 34, 38, 39, 45. eop: 8. eval : \underline{116}, 117. eval two bytes : \underline{25}. exit : \underline{5}, 114, 118, 131, 133. ext tag : \underline{16}, 100. exten: 16, \underline{28}, 108. exten base: \underline{26}, 27, 28, 109. : 107. : 109. extensible recipe: 13, 18. extra space: 19. f: \underline{62}, \underline{116}, \underline{117}, \underline{132}, \underline{133}. face: 14, 49, 61. false: 33, 69, 89, 91, 118, 119, 128, 129, 130,

131, 133. family : \underline{28}, 77. family name: 14. : 33. final end : \underline{3}, 24, 31, 100, 112, 126, 131, 133, 134. fix word : 7, 8, \underline{13}, 14, 19, 28, 62, 82, 84, 120. fnt def1 : 7, \underline{8}, 35. fnt def2 : \underline{7}. fnt def3 : \underline{7}. fnt def4 : \underline{7}. fnt num 0 : \underline{8}, 128. fnt num0 : 8. fnt1 : \underline{8}, 128. fnt4 : 8. font identifier: 14. : 44. font bc: \underline{37}, 39. font chars : \underline{30}, 39, 129. font ec: \underline{37}, 39. font lh: \underline{37}, 39. font number : \underline{30}, 35, 128. font ptr : \underline{30}, 33, 35, 36, 39, 40, 41, 44, 121,

128, 129. font start : \underline{30}, 33, 35, 36, 40, 41, 44, 119,

121, 122, 129. font type: \underline{29}, 60, 70, 75, 81, 83. forward : 116, 117. four cases : \underline{126}, 127, 128, 129, 130. Fuchs, David Raymond: 1. get bytes : \underline{125}, 126, 127, 128, 129, 130. get vf : \underline{45}. h: \underline{111}, \underline{116}. hash: \underline{111}, 112, 114, 116, 117. hash input : 113, \underline{114}. hash list : \underline{111}, 112, 114. hash ptr : \underline{111}, 112, 114. hash size: \underline{4}, 111, 112, 114, 116. header : 14. height : 15, \underline{28}, 102. : 102. : 84. height base: \underline{26}, 27, 28, 84. height index : 15, \underline{28}, 100, 102. hh: \underline{111}, 112. i: \underline{69}, \underline{132}. : 35. : 39. id byte: \underline{7}, 32. : 35. : 122. : 122. improper DVI for VF : \underline{8}, 124. : 25. : 46. incr : \underline{5}, 32, 33, 34, 35, 39, 44, 56, 57, 58, 59, 63, 90, 94, 97, 100, 114, 118, 124, 125, 126, 128, 129, 130.

index : \underline{22}, 47, 57, 58, 61, 62, 69, 74, 116,

117, 131, 133. : 112. initialize: \underline{2}, 134. integer : 2, 26, 30, 37, 45, 47, 52, 62, 114, 116,

118, 120, 123, 125, 131, 132, 133. italic: 15, \underline{28}, 104. : 104. : 84. italic base: \underline{26}, 27, 28, 84. italic index : 15, \underline{28}, 100, 104. j: \underline{53}, \underline{58}, \underline{62}, \underline{74}, \underline{118}. k: \underline{2}, \underline{45}, \underline{47}, \underline{57}, \underline{58}, \underline{61}, \underline{62}, \underline{69}, \underline{74}, \underline{118}, \underline{120},

\underline{125}, \underline{131}, \underline{132}, \underline{133}.

kern: 17, \underline{28}, 84, 98.

: 98. : 84. : 98. kern base: \underline{26}, 27, 28. kern flag : \underline{17}, 96, 98, 115. key : \underline{114}, \underline{116}. l: \underline{37}, \underline{56}, \underline{57}, \underline{58}, \underline{74}, \underline{118}. label ptr : \underline{85}, 86, 89, 90, 91. label table: \underline{85}, 86, 89, 90, 91, 94. left : \underline{56}, 71, 73, 76, 77, 78, 79, 80, 82, 88, 91, 94, 95, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 119, 121, 122, 124, 126, 129, 130.

left z : \underline{111}, 115, 117.

level : \underline{54}, 55, 56, 93, 95, 96, 134.

lf : \underline{12}, 22, 24, 25.

lh: \underline{12}, 13, 25, 27, 70, 78, 79. : 92. lig kern: 4, 16, 17. lig kern base: \underline{26}, 27, 28. lig kern command : 13, 17. lig size: \underline{4}, 25, 85, 87, 89, 133. lig step: \underline{28}, 89, 91, 92, 96, 105, 112, 113, 115. lig tag : \underline{16}, 85, 89, 100, 112. lig z : \underline{111}, 114, 117. : 99. : 99. : 96. : 89, 91. list tag : \underline{16}, 100, 106. long char : \underline{8}, 33, 46. : 35. mathex : \underline{29}, 75, 81, 83. mathsy : \underline{29}, 75, 81, 83. max fonts : \underline{4}, 30, 35. max stack : \underline{4}, 123, 126. MBL string : \underline{49}, 50, 61. mid : 18. : 124. : 126. : 124. name length: \underline{4}, 37, 44. nd : \underline{12}, 13, 25, 27, 84, 103. ne: \underline{12}, 13, 25, 27, 107, 109. : 46. next char : 17. nh: \underline{12}, 13, 25, 27, 84, 102. ni : \underline{12}, 13, 25, 27, 84, 104. nil: 5. nk : \underline{12}, 13, 25, 27, 84, 98. nl : \underline{12}, 13, 17, 25, 27, 88, 89, 91, 92, 93, 96,

105, 112, 113. no tag : \underline{16}, 28, 100. nonexistent : \underline{28}, 46, 98, 99, 106, 108, 109. : 74. nonzero fix : \underline{84}. nop: \underline{8}, 126. not found : \underline{5}, 118. np: \underline{12}, 13, 25, 80, 81. num1 : 19. num2 : 19. num3 : 19. nw : \underline{12}, 13, 25, 27, 84, 101. o: \underline{123}. odd : 99. : 25. op byte: 17. organize: \underline{131}, 134. out : \underline{48}, 52, 56, 57, 58, 60, 61, 62, 64, 65, 71,

72, 73, 76, 77, 78, 79, 80, 82, 83, 88, 91, 94, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 112, 119, 121, 122, 124, 126, 127, 128, 129, 130, 134.

out as fix : \underline{120}, 126, 127. out BCPL: \underline{57}, 76, 77. out char : \underline{60}, 91, 94, 98, 99, 100, 106, 108, 129. out digs : \underline{52}, 58, 63. out face: \underline{61}, 78. out fix : \underline{62}, 73, 82, 98, 101, 102, 103, 104, 120, 121. out hex : \underline{130}. out ln: \underline{56}, 73, 80, 88, 95, 97, 100, 105, 107, 121,

124, 126, 127, 128, 130. out octal : \underline{58}, 60, 61, 71, 78, 121. output : \underline{2}. : 120. packet end : \underline{30}, 46, 124. packet found : \underline{30}, 33, 35, 46. packet start : \underline{30}, 33, 46, 124. param: 14, 19, \underline{28}, 82. param base: \underline{26}, 27, 28. : 82. : 74. pass through: \underline{87}, 89, 91, 93. pending : \underline{111}, 117. perfect : \underline{67}, 68, 69, 89, 91, 119, 134. pl : 8, \underline{30}, 46. pop: \underline{8}, 126. post : \underline{8}, 9, 33, 34. pre: 7, \underline{8}, 31, 35. print : \underline{2}, 32, 36, 52, 53, 69, 89, 91, 100, 106, 112. print digs : \underline{52}, 53. print ln: \underline{2}, 24, 31, 32, 33, 34, 36, 39, 40, 41,

46, 69, 72, 81, 82, 89, 91, 92, 99, 100, 106, 112, 119, 126, 134.

print octal : \underline{53}, 69, 89, 100, 106, 112. push: \underline{8}, 126. put rule: \underline{8}, 126. put1 : \underline{8}, 129. quad : 19. r: \underline{69}. random word : \underline{28}, 78, 79. range error : \underline{69}, 101, 102, 103, 104, 107. RCE string : \underline{49}, 50, 61. read : 24, 31, 38. read tfm: \underline{38}. read tfm word : \underline{38}, 39. read vf : \underline{31}, 32, 33, 34, 45. real : 30. real dsize: \underline{30}, 32, 36. remainder : 15, 16, 17, \underline{28}, 89, 105, 106, 107, 112. rep: 18. reset : 11, 39. reset tag : \underline{28}, 89, 106, 107. return: \underline{5}. rewrite: 21. RI string : \underline{49}, 50, 61. right : \underline{56}, 71, 73, 76, 77, 78, 79, 80, 82, 88, 91,

93, 94, 95, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 119, 121, 122, 124, 126, 129, 130.

right z : \underline{111}, 115, 117. right1 : \underline{8}, 127. rr : \underline{85}, 86, 89, 90, 91, 94. s: \underline{61}. scheme: \underline{28}, 75, 76. set char 0 : \underline{8}, 129. set rule: \underline{8}, 126. set1 : \underline{8}, 129. seven bit safe flag : 14, 79. short char0 : 8. short char241 : 8. : 84. signed : \underline{125}. simple: \underline{111}, 112, 114, 115, 117. sixteen cases : \underline{126}. sixty four cases : \underline{126}, 128, 129. skip byte: 17. slant : \underline{19}. : 112. sort ptr : \underline{85}, 90, 93, 94, 100. space: 19. space shrink : 19. space stretch: 19. : 126. stop flag : \underline{17}, 89, 92, 96, 97, 105, 112, 113. : 74. string balance: \underline{118}, 119, 122, 130. stuff : 13. subdrop: 19. : 25. sub1 : 19. sub2 : 19. supdrop: 19. sup1 : 19. sup2 : 19. sup3 : 19. system dependencies: 2, 11, 36, 38, 39, 42,

44, 60, 135. t: \underline{114}. tag : 15, 16, \underline{28}, 85, 89, 100, 106, 112. temp byte: \underline{30}, 31, 32, 33, 34, 35, 46. text : 20. tfm: 4, 8, 22, \underline{23}, 24, 25, 26, 28, 32, 46, 47, 57,

58, 59, 60, 61, 62, 69, 73, 74, 75, 79, 82, 84, 89, 91, 92, 96, 97, 98, 99, 105, 108, 109, 112, 113, 115, 118, 120, 121, 131.

´ : 39. tfm file: \underline{2}, \underline{10}, 11, 22, 24, 38, 39. tfm ptr : 24, 25, \underline{131}. tfm size: \underline{4}, 22, 23, 24. tfm width: 46, \underline{47}. : 25. : 24. : 32. : 24. : 24, 32. : 24, 31. : 25. : 24, 32. : 25. : 134. : 25. : 24, 34. thirty two cases : \underline{126}. : 134. : 119. top: 18, \underline{123}, 124, 126, 127. : 39. true: 46, 68, 118, 126, 127, 131, 133. : 128. unreachable: \underline{87}, 88, 89, 95. : 81. vanilla: \underline{29}, 60, 70, 75. vf : 4, \underline{30}, 32, 35, 36, 39, 40, 41, 44, 118, 119, 121,

122, 124, 125, 129, 130, 131. : 34. vf abort : \underline{31}, 32, 35, 39, 44, 46. vf count : \underline{30}, 32, 33, 34, 45. vf file: \underline{2}, \underline{7}, 11, 30, 31, 32, 33, 34, 45. vf input : \underline{131}. vf limit : \underline{123}, 124, 125, 130. vf ptr : \underline{30}, 32, 33, 35, 36, 39, 44, 46, 124,

125, 130, \underline{131}. vf read : 35, \underline{45}, 46. vf size: \underline{4}, 30, 32, 33, 39, 46, 123, 124, 131,

132, 133. vf store: \underline{32}, 35, 46. VFtoVP : \underline{2}. vpl file: \underline{2}, \underline{20}, 21, 48, 56. width: 15, \underline{28}, 47, 84, 101. : 84. width base: \underline{26}, 27, 28, 84. width index : 15, \underline{28}, 100, 101. write: 2, 48. write ln: 2, 56. : 32. wstack : \underline{123}, 124, 126, 127. w0 : \underline{8}, 127. w1 : \underline{8}, 127. x: \underline{116}, \underline{120}. x height : 19. x lig cycle: \underline{111}, 112, 117. xchr : 32, 36, 44, \underline{49}, 50, 57, 60, 119, 122, 130. xstack : \underline{123}, 124, 126, 127. xxx1 : \underline{8}, 130. xxx4 : \underline{8}. x0 : \underline{8}, 127. x1 : \underline{8}, 127. y: \underline{114}, \underline{116}. y lig cycle: \underline{111}, 112, 117. ystack : \underline{123}, 124, 126, 127. y0 : \underline{8}, 127. y1 : \underline{8}, 127. zstack : \underline{123}, 124, 126, 127. zz : \underline{114}, 115. z0 : \underline{8}, 127. z1 : \underline{8}, 127.

156  NAMES OF THE SECTIONS  VFtoVP §136

⟨ Build the label table 89 ⟩ Used in section 88. ⟨ Cases of instructions that can appear in character packets 126, 127, 128, 129, 130 ⟩ Used in section 124. ⟨ Check and output the ith parameter 82 ⟩ Used in section 80. ⟨ Check for a boundary char 91 ⟩ Used in section 88. ⟨ Check for ligature cycles 112 ⟩ Used in section 88. ⟨ Check the check sum 40 ⟩ Used in section 39. ⟨ Check the design size 41 ⟩ Used in section 39. ⟨ Check the extensible recipes 109 ⟩ Used in section 134.

⟨ Check the fix word entries 84 ⟩ Used in section 132.

⟨ Check to see if np is complete for this font type 81 ⟩ Used in section 80. ⟨ Compute the base addresses 27 ⟩ Used in section 131. ⟨ Compute the command parameters y, cc, and zz 115 ⟩ Used in section 114.

⟨ Compute the activity array 92 ⟩ Used in section 88.

⟨ Constants in the outer block 4 ⟩ Used in section 2.

⟨ Do the characters 100 ⟩ Used in section 133. ⟨ Do the header 70 ⟩ Used in section 132.

⟨ Do the ligatures and kerns 88 ⟩ Used in section 134.

⟨ Do the local fonts 121 ⟩ Used in section 132.

⟨ Do the packet for character c 124 ⟩ Used in section 133.

⟨ Do the parameters 80 ⟩ Used in section 132.

⟨ Do the virtual font title 119 ⟩ Used in section 132. ⟨ Enter data for character c starting at location i in the hash table 113 ⟩ Used in sections 112 and 112.

⟨ Globals in the outer block 7, 10, 12, 20, 23, 26, 29, 30, 37, 42, 49, 51, 54, 67, 69, 85, 87, 111, 123 ⟩ Used in section 2. ⟨ Insert (c, r) into label table 90 ⟩ Used in section 89.

⟨ Labels in the outer block 3 ⟩ Used in section 2. ⟨ Move font name into the cur name string 44 ⟩ Used in section 39.

⟨ Output a kern step 98 ⟩ Used in section 96.

⟨ Output a ligature step 99 ⟩ Used in section 96. ⟨ Output an extensible character recipe 107 ⟩ Used in section 100. ⟨ Output and correct the ligature/kern program 93 ⟩ Used in section 88. ⟨ Output any labels for step i 94 ⟩ Used in section 93.

⟨ Output either  or  97 ⟩ Used in section 96.

⟨ Output step i of the ligature/kern program 96 ⟩ Used in sections 93 and 105. ⟨ Output the applicable part of the ligature/kern program as a comment 105 ⟩ Used in section 100. ⟨ Output the character coding scheme 76 ⟩ Used in section 70. ⟨ Output the character link unless there is a problem 106 ⟩ Used in section 100. ⟨ Output the character’s depth 103 ⟩ Used in section 100. ⟨ Output the character’s height 102 ⟩ Used in section 100. ⟨ Output the character’s width 101 ⟩ Used in section 100. ⟨ Output the check sum 71 ⟩ Used in section 70. ⟨ Output the design size 73 ⟩ Used in section 70. ⟨ Output the extensible pieces that exist 108 ⟩ Used in section 107. ⟨ Output the family name 77 ⟩ Used in section 70. ⟨ Output the font area and name 122 ⟩ Used in section 121. ⟨ Output the fraction part, f/2^{20}, in decimal notation 64 ⟩ Used in section 62. ⟨ Output the integer part, a, in decimal notation 63 ⟩ Used in section 62. ⟨ Output the italic correction 104 ⟩ Used in section 100. ⟨ Output the name of parameter i 83 ⟩ Used in section 82. ⟨ Output the rest of the header 78 ⟩ Used in section 70.

⟨ Output the seven bit safe flag 79 ⟩ Used in section 70.

⟨ Print the name of the local font 36 ⟩ Used in section 35. ⟨ Read and store a character packet 46 ⟩ Used in section 33.

§136  VFtoVP  NAMES OF THE SECTIONS 157

⟨ Read and store a font definition 35 ⟩ Used in section 33. ⟨ Read and store the font definitions and character packets 33 ⟩ Used in section 31. ⟨ Read and verify the postamble 34 ⟩ Used in section 31. ⟨ Read the local font’s file and record the characters it contains 39 ⟩ Used in section 35. ⟨ Read the preamble command 32 ⟩ Used in section 31.

⟨ Read the whole file 24 ⟩ Used in section 131. ⟨ Read the whole file 31 ⟩ Used in section 131.

⟨ Reduce l by one, preserving the invariants 59 ⟩ Used in section 58. ⟨ Reduce negative to positive 65 ⟩ Used in section 62.

⟨ Set initial values 11, 21, 43, 50, 55, 68, 86 ⟩ Used in section 2.

⟨ Set subfile sizes lh, bc, …, np 25 ⟩ Used in section 131.

⟨ Set the true font type 75 ⟩ Used in section 70.

⟨ Take care of commenting out unreachable steps 95 ⟩ Used in section 93. ⟨ Types in the outer block 5, 22 ⟩ Used in section 2.

The VFtoVP processor

(Version 1.2, September 1990)

Section Page

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 102 Virtual fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 104 Font metric data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 107 Unpacking the TFM file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 113 Unpacking the VF file . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 116 Basic output subroutines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 122 Outputting the TFM info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 126 Checking for ligature loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 139 Outputting the VF info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 143 The main program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 148 System-dependent changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 150 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 151

The preparation of this program was supported in part by the National Science Foundation and by the System Development Foundation. ‘TEX’ is a trademark of the American Mathematical Society.