diff options
author | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2021-02-25 19:22:25 +0000 |
commit | ad547a6b5986815fda458221149728d9d9ab1d87 (patch) | |
tree | 16296910eb3eca724371474ea9aea3994dc69614 /Build/source/texk/web2c/gftodvi.web | |
parent | 947b43de3dd21d58ccc2ffadefc4441ea1c2a813 (diff) |
restore Build,TODO from r57911
git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c/gftodvi.web')
-rw-r--r-- | Build/source/texk/web2c/gftodvi.web | 4393 |
1 files changed, 4393 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/gftodvi.web b/Build/source/texk/web2c/gftodvi.web new file mode 100644 index 00000000000..9fb263a7e35 --- /dev/null +++ b/Build/source/texk/web2c/gftodvi.web @@ -0,0 +1,4393 @@ +% This program by D. E. Knuth is not copyrighted and can be used freely. +% Version 0 was completed on April 23, 1984. +% Version 0.1 added char_code output (May 4). +% Version 0.2 included rules and dots in the boundary calculations (May 25). +% Version 0.3 added label type "/" (May 27). +% Version 0.4 (by Arthur Samuel) improved the dot labeling routine (July 23). +% Version 0.5 added the slant font for rules (September 2). +% Version 0.6 changed label types and allowed invisible dots (September 28). +% Version 1.0 switched to new GF format (December 8). +% Version 1.1 switched to newer GF format (February 2, 1985). +% Version 1.2 added the offset operations of MF version 0.8 (April 1, 1985). +% Version 1.3 allowed online entry of gray font, etc. (April 22, 1985). +% Version 1.4 allowed "almost" horizontal or vertical rules (May 20, 1985). +% Version 1.5 corrected a bug in the diagonal slant routine (June 18, 1985). +% Version 1.6 corrected a bug if labels exist but no dots (September 13, 1985). +% Version 1.7 changed from am to cm fonts; fam became ext (October 5, 1985). +% Version 2.0 was tuned up for the METAFONTware report (April, 1989). +% Version 3.0 uses 8-bit codes and extended ligatures (October, 1989). + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\indent\ignorespaces} +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like SAIL +\def\PASCAL{Pascal} +\font\logo=manfnt % font used for the METAFONT logo +\def\MF{{\logo META}\-{\logo FONT}} +\let\swap=\leftrightarrow + +\def\(#1){} % this is used to make section names sort themselves better +\def\9#1{} % this is used for sort keys in the index + +\def\title{GF$\,$\lowercase{to}$\,$DVI} +\def\contentspagenumber{301} +\def\topofcontents{\null + \titlefalse % include headline on the contents page + \def\rheader{\mainfont\hfil \contentspagenumber} + \vfill + \centerline{\titlefont The {\ttitlefont GFtoDVI} processor} + \vskip 15pt + \centerline{(Version 3.0, October 1989)} + \vfill} +\def\botofcontents{\vfill + \centerline{\hsize 5in\baselineskip9pt + \vbox{\ninerm\noindent + The preparation of this report + was supported in part by the National Science + Foundation under grants IST-8201926, MCS-8300984, and + CCR-8610181, + and by the System Development Foundation. `\TeX' is a + trademark of the American Mathematical Society. + `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley + Publishing Company.}}} +\pageno=\contentspagenumber \advance\pageno by 1 + +@* Introduction. +The \.{GFtoDVI} utility program reads binary generic font (``\.{GF}'') +files that are produced by font compilers such as \MF, and converts them +into device-independent (``\.{DVI}'') files that can be printed to give +annotated hardcopy proofs of the character shapes. The annotations are +specified by the comparatively simple conventions of plain \MF; i.e., +there are mechanisms for labeling chosen points and for superimposing +horizontal or vertical rules on the enlarged character shapes. + +The purpose of \.{GFtoDVI} is simply to make proof copies; it does not +exhaustively test the validity of a \.{GF} file, nor do its algorithms +much resemble the algorithms that are typically used to prepare font +descriptions for commercial typesetting equipment. Another program, +\.{GFtype}, is available for validity checking; \.{GFtype} also serves +as a model of programs that convert fonts from \.{GF} format to some +other coding scheme. + +The |banner| string defined here should be changed whenever \.{GFtoDVI} +gets modified. + +@d banner=='This is GFtoDVI, Version 3.0' {printed when the program starts} + +@ This program is written in standard \PASCAL, except where it is necessary +to use extensions; for example, \.{GFtoDVI} must read files whose names +are dynamically specified, and such a task would be impossible in pure \PASCAL. +All places where nonstandard constructions are used have been listed in +the index under ``system dependencies.'' +@!@^system dependencies@> + +Another exception to standard \PASCAL\ occurs in the +use of default branches in |case| statements; the conventions +of \.{TANGLE}, \.{WEAVE}, etc., have been followed. + +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + +@ The main input and output files are not mentioned in the program header, +because their external names +will be determined at run time (e.g., by interpreting the +command line that invokes this program). 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. +@^system dependencies@> + +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. + +@d print(#)==write(#) +@d print_ln(#)==write_ln(#) +@d print_nl(#)==@+begin write_ln; write(#);@+end + +@p program GF_to_DVI(@!output); +label @<Labels in the outer block@>@/ +const @<Constants in the outer block@>@/ +type @<Types in the outer block@>@/ +var @<Globals in the outer block@>@/ +procedure initialize; {this procedure gets things started properly} + var @!i,@!j,@!m,@!n:integer; {loop indices for initializations} + begin print_ln(banner);@/ + @<Set initial values@>@/ + end; + +@ If the program has to stop prematurely, it goes to the +`|final_end|'. + +@d final_end=9999 {label for the end of it all} + +@<Labels...@>=final_end; + +@ The following parameters can be changed at compile time to extend or +reduce \.{GFtoDVI}'s capacity. + +@<Constants...@>= +@!max_labels=2000; {maximum number of labels and dots and rules per character} +@!pool_size=10000; {maximum total length of labels and other strings} +@!max_strings=1100; {maximum number of labels and other strings} +@!terminal_line_length=150; {maximum number of characters input in a single + line of input from the terminal} +@!file_name_size=50; {a file name shouldn't be longer than this} +@!font_mem_size=2000; {space for font metric data} +@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8} +@!widest_row=8192; {maximum number of pixels per row} +@!lig_lookahead=20; {size of stack used when inserting ligature characters} + +@ Labels are given symbolic names by the following definitions, so that +occasional |goto| statements will be meaningful. We insert the label +`|exit|:' just before the `\ignorespaces|end|\unskip' of a procedure in +which we have used the `|return|' statement defined below; the label +`|reswitch|' is occasionally used just prior to a |case| +statement in which some cases change the conditions and we wish to branch +to the newly applicable case. Loops that are set up with the |loop| +construction defined below are commonly exited by going to `|done|' or to +`|found|' or to `|not_found|', and they are sometimes repeated by going to +`|continue|'. + +Incidentally, this program never declares a label that isn't actually used, +because some fussy \PASCAL\ compilers will complain about redundant labels. + +@d exit=10 {go here to leave a procedure} +@d reswitch=21 {go here to start a case statement again} +@d continue=22 {go here to resume a loop} +@d done=30 {go here to exit a loop} +@d done1=31 {like |done|, when there is more than one loop} +@d found=40 {go here when you've found it} +@d not_found=45 {go here when you've found nothing} + +@ Here are some macros for common programming idioms. + +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@d loop == @+ while true do@+ {repeat over and over until a |goto| happens} +@f loop == xclause + {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'} +@d do_nothing == {empty statement} +@d return == goto exit {terminate a procedure call} +@f return == nil {\.{WEB} will henceforth say |return| instead of \\{return}} + +@ If the \.{GF} file is badly malformed, the whole process must be aborted; +\.{GFtoDVI} will give up, after issuing an error message about the symptoms +that were noticed. + +Such errors might be discovered inside of subroutines inside of subroutines, +so a procedure called |jump_out| has been introduced. This procedure, which +simply transfers control to the label |final_end| at the end of the program, +contains the only non-local |goto| statement in \.{GFtoDVI}. +@^system dependencies@> + +@d abort(#)==@+begin print(' ',#); jump_out;@+end +@d bad_gf(#)==abort('Bad GF file: ',#,'! (at byte ',cur_loc-1:1,')') +@.Bad GF file@> + +@p procedure jump_out; +begin goto final_end; +end; + +@ As in \TeX\ and \MF, this program deals with numeric quantities that +are integer multiples of~$2^{16}$, and calls them |scaled|. + +@d unity==@'200000 {|scaled| representation of 1.0} + +@<Types ...@>= +@!scaled=integer; {fixed-point numbers} + +@* The character set. +Like all programs written with the \.{WEB} system, \.{GFtoDVI} can be +used with any character set. But it uses ASCII code internally, because +the programming for portable input-output is easier when a fixed internal +code is used. Furthermore, both \.{GF} and \.{DVI} files use ASCII code +for file names and certain other strings. +The next few sections of \.{GFtoDVI} have therefore been copied from the +analogous ones in the \.{WEB} system routines. + +@<Types ...@>= +@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers} + +@ The original \PASCAL\ compiler was designed in the late 60s, when +six-bit character sets were common, so it did not make provision for lowercase +letters. Nowadays, of course, we need to deal with both capital and +small letters in a convenient way. So we shall assume that the +\PASCAL\ system being used for \.{GFtoDVI} has a character set containing +at least the standard visible ASCII characters (|"!"| through |"~"|). If +additional characters are present, \.{GFtoDVI} can be configured to +work with them too. + +Some \PASCAL\ compilers use the original name |char| for the data type +associated with the characters in text files, while other \PASCAL s +consider |char| to be a 64-element subrange of a larger data type that has +some other name. In order to accommodate this difference, we shall use +the name |text_char| to stand for the data type of the characters in the +output file. We shall also assume that |text_char| consists of +the elements |chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if necessary. +@^system dependencies@> + +@d text_char == char {the data type of characters in text files} +@d first_text_char=0 {ordinal number of the smallest element of |text_char|} +@d last_text_char=255 {ordinal number of the largest element of |text_char|} + +@<Types ...@>= +@!text_file=packed file of text_char; + +@ The \.{GFtoDVI} processor converts between ASCII code and +the user's external character set by means of arrays |xord| and |xchr| +that are analogous to \PASCAL's |ord| and |chr| functions. + +@<Globals...@>= +@!xord: array [text_char] of ASCII_code; + {specifies conversion of input characters} +@!xchr: array [ASCII_code] of text_char; + {specifies conversion of output characters} + +@ Under our assumption that the visible characters of standard ASCII are +all present, the following assignment statements initialize the +|xchr| array properly, without needing any system-dependent changes. + +@<Set init...@>= +xchr[@'40]:=' '; +xchr[@'41]:='!'; +xchr[@'42]:='"'; +xchr[@'43]:='#'; +xchr[@'44]:='$'; +xchr[@'45]:='%'; +xchr[@'46]:='&'; +xchr[@'47]:='''';@/ +xchr[@'50]:='('; +xchr[@'51]:=')'; +xchr[@'52]:='*'; +xchr[@'53]:='+'; +xchr[@'54]:=','; +xchr[@'55]:='-'; +xchr[@'56]:='.'; +xchr[@'57]:='/';@/ +xchr[@'60]:='0'; +xchr[@'61]:='1'; +xchr[@'62]:='2'; +xchr[@'63]:='3'; +xchr[@'64]:='4'; +xchr[@'65]:='5'; +xchr[@'66]:='6'; +xchr[@'67]:='7';@/ +xchr[@'70]:='8'; +xchr[@'71]:='9'; +xchr[@'72]:=':'; +xchr[@'73]:=';'; +xchr[@'74]:='<'; +xchr[@'75]:='='; +xchr[@'76]:='>'; +xchr[@'77]:='?';@/ +xchr[@'100]:='@@'; +xchr[@'101]:='A'; +xchr[@'102]:='B'; +xchr[@'103]:='C'; +xchr[@'104]:='D'; +xchr[@'105]:='E'; +xchr[@'106]:='F'; +xchr[@'107]:='G';@/ +xchr[@'110]:='H'; +xchr[@'111]:='I'; +xchr[@'112]:='J'; +xchr[@'113]:='K'; +xchr[@'114]:='L'; +xchr[@'115]:='M'; +xchr[@'116]:='N'; +xchr[@'117]:='O';@/ +xchr[@'120]:='P'; +xchr[@'121]:='Q'; +xchr[@'122]:='R'; +xchr[@'123]:='S'; +xchr[@'124]:='T'; +xchr[@'125]:='U'; +xchr[@'126]:='V'; +xchr[@'127]:='W';@/ +xchr[@'130]:='X'; +xchr[@'131]:='Y'; +xchr[@'132]:='Z'; +xchr[@'133]:='['; +xchr[@'134]:='\'; +xchr[@'135]:=']'; +xchr[@'136]:='^'; +xchr[@'137]:='_';@/ +xchr[@'140]:='`'; +xchr[@'141]:='a'; +xchr[@'142]:='b'; +xchr[@'143]:='c'; +xchr[@'144]:='d'; +xchr[@'145]:='e'; +xchr[@'146]:='f'; +xchr[@'147]:='g';@/ +xchr[@'150]:='h'; +xchr[@'151]:='i'; +xchr[@'152]:='j'; +xchr[@'153]:='k'; +xchr[@'154]:='l'; +xchr[@'155]:='m'; +xchr[@'156]:='n'; +xchr[@'157]:='o';@/ +xchr[@'160]:='p'; +xchr[@'161]:='q'; +xchr[@'162]:='r'; +xchr[@'163]:='s'; +xchr[@'164]:='t'; +xchr[@'165]:='u'; +xchr[@'166]:='v'; +xchr[@'167]:='w';@/ +xchr[@'170]:='x'; +xchr[@'171]:='y'; +xchr[@'172]:='z'; +xchr[@'173]:='{'; +xchr[@'174]:='|'; +xchr[@'175]:='}'; +xchr[@'176]:='~'; + +@ Here now is the system-dependent part of the character set. +If \.{GFtoDVI} is being implemented on a garden-variety \PASCAL\ for which +only standard ASCII codes will appear in the input and output files, you +don't need to make any changes here. But if you have, for example, an extended +character set like the one in Appendix~C of {\sl The \TeX book}, the first +line of code in this module should be changed to +$$\hbox{|for i:=0 to @'37 do xchr[i]:=chr(i);|}$$ +\.{WEB}'s character set is essentially identical to \TeX's. +@^system dependencies@> + +@<Set init...@>= +for i:=0 to @'37 do xchr[i]:='?'; +for i:=@'177 to @'377 do xchr[i]:='?'; + +@ The following system-independent code makes the |xord| array contain a +suitable inverse to the information in |xchr|. + +@<Set init...@>= +for i:=first_text_char to last_text_char do xord[chr(i)]:=" "; +for i:=1 to @'377 do xord[xchr[i]]:=i; +xord['?']:="?"; + +@ The |input_ln| routine waits for the user to type a line at his or her +terminal; then it puts ASCII-code equivalents for the characters on that line +into the |buffer| array. The |term_in| file is used for terminal input. +@^system dependencies@> + +Since the terminal is being used for both input and output, some systems +need a special routine to make sure that the user can see a prompt message +before waiting for input based on that message. (Otherwise the message +may just be sitting in a hidden buffer somewhere, and the user will have +no idea what the program is waiting for.) We shall call a system-dependent +subroutine |update_terminal| in order to avoid this problem. + +@d update_terminal == break(output) {empty the terminal output buffer} + +@<Glob...@>= +@!buffer:array[0..terminal_line_length] of 0..255; +@!term_in:text_file; {the terminal, considered as an input file} + +@ A global variable |line_length| records the first buffer position after +the line just read. +@^system dependencies@> + +@p procedure input_ln; {inputs a line from the terminal} +begin update_terminal; reset(term_in); +if eoln(term_in) then read_ln(term_in); +line_length:=0; +while (line_length<terminal_line_length)and not eoln(term_in) do + begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in); + end; +end; + +@ The global variable |buf_ptr| is used while scanning each line of input; +it points to the first unread character in |buffer|. + +@<Glob...@>= +@!buf_ptr:0..terminal_line_length; {the number of characters read} +@!line_length:0..terminal_line_length; {end of line read by |input_ln|} + +@* Device-independent file format. +Before we get into the details of \.{GFtoDVI}, we need to know exactly +what \.{DVI} files are. The form of such files was designed by David R. +@^Fuchs, David Raymond@> +Fuchs in 1979. Almost any reasonable typesetting device can be driven by +a program that takes \.{DVI} files as input, and dozens of such +\.{DVI}-to-whatever programs have been written. Thus, it is possible to +print the output of document compilers like \TeX\ on many different kinds +of equipment. (The following material has been copied almost verbatim from the +program for \TeX.) + +A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a +series of commands in a machine-like language. The first byte of each command +is the operation code, and this code is followed by zero or more bytes +that provide parameters to the command. The parameters themselves may consist +of several consecutive bytes; for example, the `|set_rule|' command has two +parameters, each of which is four bytes long. Parameters are usually +regarded as nonnegative integers; but four-byte-long parameters, +and shorter parameters that denote distances, can be +either positive or negative. Such parameters are given in two's complement +notation. For example, a two-byte-long distance parameter has a value between +$-2^{15}$ and $2^{15}-1$. +@.DVI {\rm files}@> + +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. +@^BigEndian order@> + +A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one +or more ``pages,'' followed by a ``postamble.'' The preamble is simply a +|pre| command, with its parameters that define the dimensions used in the +file; this must come first. Each ``page'' consists of a |bop| command, +followed by any number of other commands that tell where characters are to +be placed on a physical page, followed by an |eop| command. The pages +appear in the order that they were generated, not in any particular +numerical order. If we ignore |nop| commands and \\{fnt\_def} commands +(which are allowed between any two commands in the file), each |eop| +command is immediately followed by a |bop| command, or by a |post| +command; in the latter case, there are no more pages in the file, and the +remaining bytes form the postamble. Further details about the postamble +will be explained later. + +Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte +quantities that give the location number of some other byte in the file; +the first byte is number~0, then comes number~1, and so on. For example, +one of the parameters of a |bop| command points to the previous |bop|; +this makes it feasible to read the pages in backwards order, in case the +results are being directed to a device that stacks its output face up. +Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the +first page occupies bytes 100 to 999, say, and if the second +page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000 +points to 100 and the |bop| that starts in byte 2000 points to 1000. (The +very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.) + +@ The \.{DVI} format is intended to be both compact and easily interpreted +by a machine. Compactness is achieved by making most of the information +implicit instead of explicit. When a \.{DVI}-reading program reads the +commands for a page, it keeps track of several quantities: (a)~The current +font |f| is an integer; this value is changed only +by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page +is given by two numbers called the horizontal and vertical coordinates, +|h| and |v|. Both coordinates are zero at the upper left corner of the page; +moving to the right corresponds to increasing the horizontal coordinate, and +moving down corresponds to increasing the vertical coordinate. Thus, the +coordinates are essentially Cartesian, except that vertical directions are +flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The +current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|, +where |w| and~|x| are used for horizontal spacing and where |y| and~|z| +are used for vertical spacing. (d)~There is a stack containing +|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to +change the current level of operation. Note that the current font~|f| is +not pushed and popped; the stack contains only information about +positioning. + +The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up +to 32 bits, including the sign. Since they represent physical distances, +there is a small unit of measurement such that increasing |h| by~1 means +moving a certain tiny distance to the right. The actual unit of +measurement is variable, as explained below. + +@ Here is a list of all the commands that may appear in a \.{DVI} file. Each +command is specified by its symbolic name (e.g., |bop|), its opcode byte +(e.g., 139), and its parameters (if any). The parameters are followed +by a bracketed number telling how many bytes they occupy; for example, +`|p[4]|' means that parameter |p| is four bytes long. + +\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f| +such that the reference point of the character is at |(h,v)|. Then +increase |h| by the width of that character. Note that a character may +have zero or negative width, so one cannot be sure that |h| will advance +after this command; but |h| usually does increase. + +\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127). +Do the operations of |set_char_0|; but use the character whose number +matches the opcode, instead of character~0. + +\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character +number~|c| is typeset. \TeX82 uses this command for characters in the +range |128<=c<256|. + +\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two +bytes long, so it is in the range |0<=c<65536|. + +\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three +bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese +language has this many characters, but this command might prove useful +in some yet unforeseen way. + +\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four +bytes long, possibly even negative. Imagine that. + +\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle +of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then +set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note +that if |b<0|, the value of |h| will decrease even though nothing else happens. + +\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f| +such that the reference point of the character is at |(h,v)|. (The `put' +commands are exactly like the `set' commands, except that they simply put out a +character or a rule without moving the reference point afterwards.) + +\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed. + +\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed. + +\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed. + +\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that +|h| is not changed. + +\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s +may occur between \.{DVI} commands, but a |nop| cannot be inserted between +a command and its parameters or between two parameters. + +\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning +of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set +the current font |f| to an undefined value. The ten $c_i$ parameters can +be used to identify pages, if a user wants to print only part of a \.{DVI} +file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9} +at the time \.{\\shipout} was invoked for this page. The parameter |p| +points to the previous |bop| command in the file, where the first |bop| +has $p=-1$. + +\yskip\hang|eop| 140. End of page: Print what you have read since the +previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading +programs that drive most output devices will have kept a buffer of the +material that appears on the page that has just ended. This material is +largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by +|h|~coordinate; so it usually needs to be sorted into some order that is +appropriate for the device in question. \.{GFtoDVI} does not do such sorting.) + +\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the +top of the stack; do not change any of these values. Note that |f| is +not pushed. + +\yskip\hang|pop| 142. Pop the top six values off of the stack and assign +them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number +of pushes, since it would be highly embarrassing if the stack were empty +at the time of a |pop| command. + +\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units. +The parameter is a signed number in two's complement notation, |-128<=b<128|; +if |b<0|, the reference point actually moves left. + +\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a +two-byte quantity in the range |-32768<=b<32768|. + +\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a +three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a +four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|w| gets particular values. + +\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128<=b<128|. This command +changes the current |w|~spacing and moves right by |b|. + +\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long +parameter, |-32768<=b<32768|. + +\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long +parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long +parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|' +commands are like the `|w|' commands except that they involve |x| instead +of |w|. + +\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128<=b<128|. This command +changes the current |x|~spacing and moves right by |b|. + +\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long +parameter, |-32768<=b<32768|. + +\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long +parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|. + +\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long +parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|. + +\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units. +The parameter is a signed number in two's complement notation, |-128<=a<128|; +if |a<0|, the reference point actually moves up. + +\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a +two-byte quantity in the range |-32768<=a<32768|. + +\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a +three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a +four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|y| gets particular values. + +\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128<=a<128|. This command +changes the current |y|~spacing and moves down by |a|. + +\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long +parameter, |-32768<=a<32768|. + +\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long +parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long +parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands +are like the `|y|' commands except that they involve |z| instead of |y|. + +\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128<=a<128|. This command +changes the current |z|~spacing and moves down by |a|. + +\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long +parameter, |-32768<=a<32768|. + +\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long +parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|. + +\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long +parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|. + +\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been +defined by a \\{fnt\_def} instruction, as explained below. + +\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set +|f:=1|, \dots, |f:=63|, respectively. + +\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font +numbers in the range |64<=k<256|. + +\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two +bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this +command, but large font numbers may prove useful for specifications of +color or texture, or they may be used for special fonts that have fixed +numbers in some external coding scheme. + +\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three +bytes long, so it can be as large as $2^{24}-1$. + +\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four +bytes long; this is for the really big font numbers (and for the negative ones). + +\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in +general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading +programs are being used. \TeX82 generates |xxx1| when a short enough +\.{\\special} appears, setting |k| to the number of bytes being sent. It +is recommended that |x| be a string having the form of a keyword followed +by possible parameters relevant to that keyword. + +\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|. + +\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|. + +\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously +large. \TeX82 uses |xxx4| when |xxx1| would be incorrect. + +\yskip\hang|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|; font definitions will be explained shortly. + +\yskip\hang|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|. + +\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0<=k<@t$2^{24}$@>|. + +\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|. + +\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|. +Beginning of the preamble; this must come at the very beginning of the +file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below. + +\yskip\hang|post| 248. Beginning of the postamble, see below. + +\yskip\hang|post_post| 249. Ending of the postamble, see below. + +\yskip\noindent Commands 250--255 are undefined at the present time. + +@ Only a few of the operation codes above are actually needed by \.{GFtoDVI}. + +@d set1=128 {typeset a character and move right} +@d put_rule=137 {typeset a rule} +@d bop=139 {beginning of page} +@d eop=140 {ending of page} +@d push=141 {save the current positions} +@d pop=142 {restore previous positions} +@d right4=146 {move right} +@d down4=160 {move down} +@d z0=166 {move down |z|} +@d z4=170 {move down and set |z|} +@d fnt_num_0=171 {set current font to 0} +@d fnt_def1=243 {define the meaning of a font number} +@d pre=247 {preamble} +@d post=248 {postamble beginning} +@d post_post=249 {postamble ending} + +@ The preamble contains basic information about the file as a whole. As +stated above, there are six parameters: +$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$ +The |i| byte identifies \.{DVI} format; currently this byte is always set +to~2. (The value |i=3| is currently used for an extended format that +allows a mixture of right-to-left and left-to-right typesetting. +Some day we will set |i=4|, when \.{DVI} format makes another +incompatible change---perhaps in the year 2048.) + +The next two parameters, |num| and |den|, are positive integers that define +the units of measurement; they are the numerator and denominator of a +fraction by which all dimensions in the \.{DVI} file could be multiplied +in order to get lengths in units of $10^{-7}$ meters. (For example, there are +exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled +points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000| +and $|den|=7227\cdot2^{16}=473628672$.) +@^sp@> + +The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the +desired magnification. The actual fraction by which dimensions are +multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\ +source document does not call for any `\.{true}' dimensions, and if you +change it only by specifying a different \.{\\mag} setting, the \.{DVI} +file that \TeX\ creates will be completely unchanged except for the value +of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow +users to override the |mag|~setting when a \.{DVI} file is being printed.) + +Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not +interpreted further. The length of comment |x| is |k|, where |0<=k<256|. + +@d dvi_id_byte=2 {identifies the kind of \.{DVI} files described here} + +@ Font definitions for a given font number |k| contain further parameters +$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ +The four-byte value |c| is the check sum that \TeX\ (or whatever program +generated the \.{DVI} file) found in the \.{TFM} file for this font; +|c| should match the check sum of the font found by programs that read +this \.{DVI} file. +@^check sum@> + +Parameter |s| contains a fixed-point scale factor that is applied to the +character widths in font |k|; font dimensions in \.{TFM} files and other +font files are relative to this quantity, which is always positive and +less than $2^{27}$. It is given in the same units as the other dimensions +of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design +size,'' and (like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be +used at $|mag|\cdot s/1000d$ times its normal size. + +The remaining part of a font definition gives the external name of the font, +which is an ASCII string of length |a+l|. The number |a| is the length +of the ``area'' or directory, and |l| is the length of the font name itself; +the standard local system font area is supposed to be used when |a=0|. +The |n| field contains the area in its first |a| bytes. + +Font definitions must appear before the first use of a particular font number. +Once font |k| is defined, it must not be defined again; however, we +shall see below that font definitions appear in the postamble as well as +in the pages, so in this sense each font number is defined exactly twice, +if at all. Like |nop| commands, font definitions can +appear before the first |bop|, or between an |eop| and a |bop|. + +@ The last page in a \.{DVI} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \TeX\ has +accumulated about the file, making it possible to print subsets of the data +with reasonable efficiency. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr + $\langle\,$font definitions$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the final |bop| in the file. The next three +parameters, |num|, |den|, and |mag|, are duplicates of the quantities that +appeared in the preamble. + +Parameters |l| and |u| give respectively the height-plus-depth of the tallest +page and the width of the widest page, in the same units as other dimensions +of the file. These numbers might be used by a \.{DVI}-reading program to +position individual ``pages'' on large sheets of film or paper; however, +the standard convention for output on normal size paper is to position each +page so that the upper left-hand corner is exactly one inch from the left +and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer +software that attempts cleverly to center the output; a fixed position of +the upper left corner is easiest for users to understand and to work with. +Therefore |l| and~|u| are often ignored. + +Parameter |s| is the maximum stack depth (i.e., the largest excess of +|push| commands over |pop| commands) needed to process this file. Then +comes |t|, the total number of pages (|bop| commands) present. + +The postamble continues with font definitions, which are any number of +\\{fnt\_def} commands as described above, possibly interspersed with |nop| +commands. Each font number that is used in the \.{DVI} file must be defined +exactly twice: Once before it is first selected by a \\{fnt} command, and once +in the postamble. + +@ The last part of the postamble, following the |post_post| byte that +signifies the end of the font definitions, contains |q|, a pointer to the +|post| command that started the postamble. An identification byte, |i|, +comes next; this currently equals~2, as in the preamble. + +The |i| byte is followed by four or more bytes that are all equal to +the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of +these trailing bytes, until the total length of the file is a multiple of +four bytes, since this works out best on machines that pack four bytes per +word; but any number of 223's is allowed, as long as there are at least four +of them. In effect, 223 is a sort of signature that is added at the very end. +@^Fuchs, David Raymond@> + +This curious way to finish off a \.{DVI} file makes it feasible for +\.{DVI}-reading programs to find the postamble first, on most computers, +even though \TeX\ wants to write the postamble last. Most operating +systems permit random access to individual words or bytes of a file, so +the \.{DVI} reader can start at the end and skip backwards over the 223's +until finding the identification byte. Then it can back up four bytes, read +|q|, and move to byte |q| of the file. This byte should, of course, +contain the value 248 (|post|); now the postamble can be read, so the +\.{DVI} reader can discover all the information needed for typesetting the +pages. Note that it is also possible to skip through the \.{DVI} file at +reasonably high speed to locate a particular page, if that proves +desirable. This saves a lot of time, since \.{DVI} files used in production +jobs tend to be large. + +Unfortunately, however, standard \PASCAL\ does not include the ability to +@^system dependencies@> +access a random position in a file, or even to determine the length of a file. +Almost all systems nowadays provide the necessary capabilities, so \.{DVI} +format has been designed to work most efficiently with modern operating systems. + +@* Generic font file format. +The ``generic font'' (\.{GF}) input files that \.{GFtoDVI} must deal with +have a structure that was inspired by \.{DVI} format, although the +operation codes are quite different in most cases. The term {\sl +generic\/} indicates that this file format doesn't match the conventions +of any name-brand manufacturer; but it is easy to convert \.{GF} files to +the special format required by almost all digital phototypesetting +equipment. There's a strong analogy between the \.{DVI} files written by +\TeX\ and the \.{GF} files written by \MF; and, in fact, the reader will +notice that many of the paragraphs below are identical to their +counterparts in the description of \.{DVI} already given. The following +description has been lifted almost verbatim from the program for \MF. + +A \.{GF} file is a stream of 8-bit bytes that may be +regarded as a series of commands in a machine-like language. The first +byte of each command is the operation code, and this code is followed by +zero or more bytes that provide parameters to the command. The parameters +themselves may consist of several consecutive bytes; for example, the +`|boc|' (beginning of character) command has six parameters, each of +which is four bytes long. Parameters are usually regarded as nonnegative +integers; but four-byte-long parameters can be either positive or +negative, hence they range in value from $-2^{31}$ to $2^{31}-1$. +As in \.{DVI} files, numbers that occupy +more than one byte position appear in BigEndian order, +and negative numbers appear in two's complement notation. + +A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or +more ``characters,'' followed by a ``postamble.'' The preamble is simply a +|pre| command, with its parameters that introduce the file; this must come +first. Each ``character'' consists of a |boc| command, followed by any +number of other commands that specify ``black'' pixels, +followed by an |eoc| command. The characters appear in the order that \MF\ +generated them. If we ignore no-op commands (which are allowed between any +two commands in the file), each |eoc| command is immediately followed by a +|boc| command, or by a |post| command; in the latter case, there are no +more characters in the file, and the remaining bytes form the postamble. +Further details about the postamble will be explained later. + +Some parameters in \.{GF} commands are ``pointers.'' These are four-byte +quantities that give the location number of some other byte in the file; +the first file byte is number~0, then comes number~1, and so on. + +@ The \.{GF} format is intended to be both compact and easily interpreted +by a machine. Compactness is achieved by making most of the information +relative instead of absolute. When a \.{GF}-reading program reads the +commands for a character, it keeps track of two quantities: (a)~the current +column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit +signed integers, although most actual font formats produced from \.{GF} +files will need to curtail this vast range because of practical +limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert +n\vert$ to get extremely large, but the \.{GF} format tries to be more general.) + +How do \.{GF}'s row and column numbers correspond to the conventions +of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's +view, is considered to be at the lower left corner of the pixel in row~0 +and column~0. This point is the intersection of the baseline with the left +edge of the type; it corresponds to location $(0,0)$ in \MF\ programs. +Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the +region of the plane whose coordinates both lie between 0 and~1. The +pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\ +coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of +|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference +point; negative values of |n| and~|y| correspond to rows of pixels {\sl +below\/} the baseline. + +Besides |m| and |n|, there's also a third aspect of the current +state, namely the @!|paint_switch|, which is always either \\{black} or +\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|, +and blackens the intervening pixels if |paint_switch=black|; then +the |paint_switch| changes to the opposite state. \.{GF}'s commands are +designed so that |m| will never decrease within a row, and |n| will never +increase within a character; hence there is no way to whiten a pixel that +has been blackened. + +@ Here is a list of all the commands that may appear in a \.{GF} file. Each +command is specified by its symbolic name (e.g., |boc|), its opcode byte +(e.g., 67), and its parameters (if any). The parameters are followed +by a bracketed number telling how many bytes they occupy; for example, +`|d[2]|' means that parameter |d| is two bytes long. + +\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does +nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa. + +\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63). +These are \\{paint} commands with |d=1| to~63, defined as follows: If +|paint_switch=black|, blacken |d|~pixels of the current row~|n|, +in columns |m| through |m+d-1| inclusive. Then, in any case, +complement the |paint_switch| and advance |m| by~|d|. + +\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified +value of~|d|; \MF\ uses it to paint when |64<=d<256|. + +\yskip\hang|paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high +as~65535. + +\yskip\hang|paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high +as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine +anybody making practical use of it; surely a more compact encoding will be +desirable when characters can be this large. But the command is there, +anyway, just in case. + +\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]| +|max_n[4]|. Beginning of a character: Here |c| is the character code, and +|p| points to the previous character beginning (if any) for characters having +this code number modulo 256. (The pointer |p| is |-1| if there was no +prior character with an equivalent code.) The values of registers |m| and |n| +defined by the instructions that follow for this character must +satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and +|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading +program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to +initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and +|paint_switch:=white|. + +\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|. +Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m| +and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|. +The one-byte parameters must be between 0 and 255, inclusive. +\ (This abbreviated |boc| saves 19~bytes per character, in common cases.) + +\yskip\hang|eoc| 69. End of character: All pixels blackened so far +constitute the pattern for this character. In particular, a completely +blank character might have |eoc| immediately following |boc|. + +\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|, +|paint_switch:=white|. \ (This finishes one row and begins another, +ready to whiten the leftmost pixel in the new row.) + +\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set +|paint_switch:=white|. This is a way to produce |d| all-white rows. + +\yskip\hang|skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large +as 65535. + +\yskip\hang|skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large +as $2^{24}-1$. \MF\ obviously never needs this command. + +\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|, +|paint_switch:=black|. \ (This finishes one row and begins another, +ready to {\sl blacken\/} the leftmost pixel in the new row.) + +\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as +|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively. + +\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in +general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading +programs are being used. \MF\ generates \\{xxx} commands when encountering +a \&{special} string; this occurs in the \.{GF} file only between +characters, after the preamble, and before the postamble. However, +\\{xxx} commands might appear within characters, +in \.{GF} files generated by other +processors. It is recommended that |x| be a string having the form of a +keyword followed by possible parameters relevant to that keyword. + +\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|. + +\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|. +\MF\ uses this when sending a \&{special} string whose length exceeds~255. + +\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be +ridiculously large; |k| mustn't be negative. + +\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general; +it functions as a 5-byte |no_op| unless special \.{GF}-reading programs +are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a +result of \&{numspecial} commands; the intent is to provide numeric +parameters to \\{xxx} commands that immediately precede. + +\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s +may occur between \.{GF} commands, but a |no_op| cannot be inserted between +a command and its parameters or between two parameters. + +\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|. +This command will appear only in the postamble, which will be explained shortly. + +\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|. +Same as |char_loc|, except that |dy| is assumed to be zero, and the value +of~|dx| is taken to be |65536*dm|, where |0<=dm<256|. + +\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|. +Beginning of the preamble; this must come at the very beginning of the +file. Parameter |i| is an identifying number for \.{GF} format, currently +131. The other information is merely commentary; it is not given +special interpretation like \\{xxx} commands are. (Note that \\{xxx} +commands may immediately follow the preamble, before the first |boc|.) + +\yskip\hang|post| 248. Beginning of the postamble, see below. + +\yskip\hang|post_post| 249. Ending of the postamble, see below. + +\yskip\noindent Commands 250--255 are undefined at the present time. + +@d gf_id_byte=131 {identifies the kind of \.{GF} files described here} + +@ Here are the opcodes that \.{GFtoDVI} actually refers to. + +@d paint_0=0 {beginning of the \\{paint} commands} +@d paint1=64 {move right a given number of columns, then + black${}\swap{}$white} +@d paint2=65 {ditto, with potentially larger number of columns} +@d paint3=66 {ditto, with potentially excessive number of columns} +@d boc=67 {beginning of a character} +@d boc1=68 {abbreviated |boc|} +@d eoc=69 {end of a character} +@d skip0=70 {skip no blank rows} +@d skip1=71 {skip over blank rows} +@d skip2=72 {skip over lots of blank rows} +@d skip3=73 {skip over a huge number of blank rows} +@d new_row_0=74 {move down one row and then right} +@d xxx1=239 {for \&{special} strings} +@d xxx2=240 {for somewhat long \&{special} strings} +@d xxx3=241 {for extremely long \&{special} strings} +@d xxx4=242 {for incredibly long \&{special} strings} +@d yyy=243 {for \&{numspecial} numbers} +@d no_op=244 {no operation} + +@ The last character in a \.{GF} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \MF\ has +accumulated. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]| + |@!min_m[4]| |@!max_m[4]| |@!min_n[4]| |@!max_n[4]|\cr + $\langle\,$character locators$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the byte following the final |eoc| in the file +(or to the byte following the preamble, if there are no characters); +it can be used to locate the beginning of \\{xxx} commands +that might have preceded the postamble. The |ds| and |cs| parameters +@^design size@> @^check sum@> +give the design size and check sum, respectively, of the font (see the +description of \.{TFM} format below). +Parameters |hppp| and |vppp| are the ratios of +pixels per point, horizontally and vertically, expressed as |scaled| integers +(i.e., multiplied by $2^{16}$); they can be used to correlate the font +with specific device resolutions, magnifications, and ``at sizes.'' Then +come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that +registers |m| and~|n| assume in all characters in this \.{GF} file. +(These bounds need not be the best possible; |max_m| and |min_n| may, on the +other hand, be tighter than the similar bounds in |boc| commands. For +example, some character may have |min_n=-100| in its |boc|, but it might +turn out that |n| never gets lower than |-50| in any character; then +|min_n| can have any value |<=-50|. If there are no characters in the file, +it's possible to have |min_m>max_m| and/or |min_n>max_n|.) + +@ Character locators are introduced by |char_loc| commands, +which specify a character residue~|c|, character escapements (|dx,dy|), +a character width~|w|, and a pointer~|p| +to the beginning of that character. (If two or more characters have the +same code~|c| modulo 256, only the last will be indicated; the others can be +located by following backpointers. Characters whose codes differ by a +multiple of 256 are assumed to share the same font metric information, +hence the \.{TFM} file contains only residues of character codes modulo~256. +This convention is intended for oriental languages, when there are many +character shapes but few distinct widths.) +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> + +The character escapements (|dx,dy|) are the values of \MF's \&{chardx} +and \&{chardy} parameters; they are in units of |scaled| pixels; +i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in +vertical pixel units times $2^{16}$. This is the intended amount of +displacement after typesetting the character; for \.{DVI} files, |dy| +should be zero, but other document file formats allow nonzero vertical +escapement. + +The character width~|w| duplicates the information in the \.{TFM} file; it +is $2^{20}$ times the ratio of the true width to the font's design size. + +The backpointer |p| points to the character's |boc|, or to the first of +a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that +immediately precede the |boc|, if such commands exist; such ``special'' +commands essentially belong to the characters, while the special commands +after the final character belong to the postamble (i.e., to the font +as a whole). This convention about |p| applies also to the backpointers +in |boc| commands, even though it wasn't explained in the description +of~|boc|. @^backpointers@> + +Pointer |p| might be |-1| if the character exists in the \.{TFM} file +but not in the \.{GF} file. This unusual situation can arise in \MF\ output +if the user had |proofing<0| when the character was being shipped out, +but then made |proofing>=0| in order to get a \.{GF} file. + +@ The last part of the postamble, following the |post_post| byte that +signifies the end of the character locators, contains |q|, a pointer to the +|post| command that started the postamble. An identification byte, |i|, +comes next; this currently equals~131, as in the preamble. + +The |i| byte is followed by four or more bytes that are all equal to +the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of +these trailing bytes, until the total length of the file is a multiple of +four bytes, since this works out best on machines that pack four bytes per +word; but any number of 223's is allowed, as long as there are at least four +of them. In effect, 223 is a sort of signature that is added at the very end. +@^Fuchs, David Raymond@> + +This curious way to finish off a \.{GF} file makes it feasible for +\.{GF}-reading programs to find the postamble first, on most computers, +even though \MF\ wants to write the postamble last. Most operating +systems permit random access to individual words or bytes of a file, so +the \.{GF} reader can start at the end and skip backwards over the 223's +until finding the identification byte. Then it can back up four bytes, read +|q|, and move to byte |q| of the file. This byte should, of course, +contain the value 248 (|post|); now the postamble can be read, so the +\.{GF} reader can discover all the information needed for individual characters. + +Unfortunately, however, standard \PASCAL\ does not include the ability to +@^system dependencies@> +access a random position in a file, or even to determine the length of a file. +Almost all systems nowadays provide the necessary capabilities, so \.{GF} +format has been designed to work most efficiently with modern operating systems. +But if \.{GF} files have to be processed under the restrictions of standard +\PASCAL, one can simply read them from front to back. This will +be adequate for most applications. However, the postamble-first approach +would facilitate a program that merges two \.{GF} files, replacing data +from one that is overridden by corresponding data in the other. + +@* Extensions to the generic format. +The \\{xxx} and \\{yyy} instructions understood by \.{GFtoDVI} will be +listed now, so that we have a convenient reference to all of the special +assumptions made later. + +Each special instruction begins with an \\{xxx} command, which consists of +either a keyword by itself, or a keyword followed by a space followed +by arguments. This \\{xxx} command may then be followed by \\{yyy} +commands that are understood to be arguments. + +The keywords of special instructions that are intended to be used at +many different sites should be published as widely as possible in order +to minimize conflicts. The first person to establish a keyword presumably +has a right to define it; \.{GFtoDVI}, as the first program +to use extended \.{GF} commands, has the opportunity of choosing any +keywords it likes, and the responsibility of choosing reasonable ones. +Since labels are expected to account for the bulk of extended commands +in typical uses of \MF, the ``null'' keyword has been set aside to +denote a labeling command. + +@ Here then are the special commands of \.{GFtoDVI}. + +\def\string{$\langle\,$string$\,\rangle$} +\def\okpagebreak{\vfil\penalty-100\vfilneg} +\smallskip\hang\noindent +\.{\SP n}\string\ $x$ $y$. Here \.n denotes the type of label; the +characters \.1, \.2, \.3,~\.4 respectively denote labels forced to be +at the top, left, right, or bottom of their dot, and the characters +\.5, \.6, \.7,~\.8 stand for the same possibilities but with no dot printed. +The character \.0 instructs \.{GFtoDVI} to choose one of the first four +possibilities, if there's no overlap with other labels or dots, otherwise +an ``overflow'' entry is placed at the right of the figure. The character +\./ is the same as \.0 except that overflow entries are not produced. The +label itself is the \string\ that follows. \MF\ coordinates of the +point that is to receive this label are given by arguments $x$ and~$y$, +in units of scaled pixels. (These arguments appear in \\{yyy} commands.) +(Precise definitions of the size and positioning of labels, and of the +notion of ``conflicting'' labels, will be given later.) + +\smallskip\hang\noindent +\.{rule} $x_1$ $y_1$ $x_2$ $y_2$. This command draws a line from +$(x_1,y_1)$ to $(x_2,y_2)$ in \MF\ coordinates. The present implementation +does this only if the line is either horizontal or vertical, or if its +slope matches the slope of the slant font. + +\smallskip\hang\noindent +\.{title\SP}\string. This command (which is output by \MF\ +when it sees a ``title statement'') specifies a string that will appear +at the top of the next proofsheet to be output by \.{GFtoDVI}. +If more than one title is given, they will appear in sequence; titles +should be short enough to fit on a single line. + +\smallskip\hang\noindent +\.{titlefont\SP}\string. This command, and the other font-naming +commands below, must precede the first |boc| in the \.{GF} file. +It overrides the current font used to +typeset the titles at the top of proofsheets. \.{GFtoDVI} has default +fonts that will be used if none other are specified; the ``current'' title +font is initially the default title font. + +\smallskip\hang\noindent +\.{titlefontarea\SP}\string. This command overrides the current +file area (or directory name) from which \.{GFtoDVI} will try to +find metric information for the title font. + +\smallskip\hang\noindent +\.{titlefontat} $s$. This command overrides the current ``at size'' that +will be used for the title font. (See the discussion of font metric files +below, for the meaning of ``at size'' versus ``design size.'') The +value of~$s$ is given in units of scaled points. + +\okpagebreak +\smallskip\hang\noindent +\.{labelfont\SP}\string. This command overrides the current font +used to typeset the labels that are superimposed on proof figures. +(The label font is fairly arbitrary, but it should be dark enough to +stand out when superimposed on gray pixels, and it should contain at +least the decimal digits and the characters `\.(', `\.)', `\.=', `\.+', +`\.-', `\.,', and `\..'.) + +\smallskip\hang\noindent +\.{labelfontarea\SP}\string. This command overrides the current +file area (or directory name) from which \.{GFtoDVI} will try to +find metric information for the label font. + +\smallskip\hang\noindent +\.{labelfontat} $s$. This command overrides the current ``at size'' that +will be used for the label font. + +\okpagebreak +\smallskip\hang\noindent +\.{grayfont\SP}\string. This command overrides the current font +used to typeset the black pixels and the dots for labels. (Gray fonts +will be explained in detail later.) +@^gray fonts@> + +\smallskip\hang\noindent +\.{grayfontarea\SP}\string. This command overrides the current +file area (or directory name) from which \.{GFtoDVI} will try to +find metric information for the gray font. + +\smallskip\hang\noindent +\.{grayfontat} $s$. This command overrides the current ``at size'' that +will be used for the gray font. + +\okpagebreak +\smallskip\hang\noindent +\.{slantfont\SP}\string. This command overrides the current font +used to typeset rules that are neither horizontal nor vertical. (Slant +fonts will be explained in detail later.) +@^slant fonts@> + +\smallskip\hang\noindent +\.{slantfontarea\SP}\string. This command overrides the current +file area (or directory name) from which \.{GFtoDVI} will try to +find metric information for the slant font. + +\smallskip\hang\noindent +\.{slantfontat} $s$. This command overrides the current ``at size'' that +will be used for the slant font. + +\okpagebreak +\smallskip\hang\noindent +\.{rulethickness} $t$. This command overrides the current value used +for the thickness of rules. If the current value is negative, no rule +will be drawn; if the current value is zero, the rule thickness will +be specified by a parameter of the gray font. Each \.{rule} command +uses the rule thickness that is current at the time the command appears; +hence it is possible to get different thicknesses of rules on the same +figure. The value of $t$ is given in units of scaled points (\TeX's `\.{sp}'). +At the beginning of each character the current rule thickness is zero. + +\smallskip\hang\noindent +\.{offset} $x$ $y$. This command overrides the current offset values +that are added to all coordinates of a character being output; $x$ and +$y$ are given as scaled \MF\ coordinates. This simply has the effect +of repositioning the figures on the pages; the title line always appears +in the same place, but the figure can be moved up, down, left, or right. +At the beginning of each character the current offsets are zero. + +\smallskip\hang\noindent +\.{xoffset} $x$. This command is output by \MF\ just before shipping out +a character whose $x$~offset is nonzero. \.{GFtoDVI} adds the specified +amount to the $x$ coordinates of all dots, labels, and rules +in the following character. + +\smallskip\hang\noindent +\.{yoffset} $y$. This command is output by \MF\ just before shipping out +a character whose $y$~offset is nonzero. \.{GFtoDVI} adds the specified +amount to the $y$ coordinates of all dots, labels, and rules +in the following character. + +@* Font metric data. +Before we can get into the meaty details of \.{GFtoDVI}, we need to +deal with yet another esoteric binary file format, since \.{GFtoDVI} +also does elementary typesetting operations. Therefore it has to +read important information about the fonts it will be using. +The following material (again copied almost verbatim from \TeX) +describes the contents of so-called \TeX\ font metric (\.{TFM}) files. + +The idea behind \.{TFM} files is that typesetting routines +need a compact way to store the relevant information about +fonts, and computer centers need a compact way to store the +relevant information about several hundred fonts. \.{TFM} files are +compact, and most of the information they contain is highly relevant, +so they provide a solution to the problem. \.{GFtoDVI} uses only +four fonts, but interesting changes in its output will occur when +those fonts are varied. + +The information in a \.{TFM} 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 \.{GFtoDVI}. The individual bytes +are considered to be unsigned numbers. + +@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit +integers that give the lengths of the various subsequent portions +of the file. These twelve integers are, in order: +$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr +|@!lf|&length of the entire file, in words;\cr +|@!lh|&length of the header data, in words;\cr +|@!bc|&smallest character code in the font;\cr +|@!ec|&largest character code in the font;\cr +|@!nw|&number of words in the width table;\cr +|@!nh|&number of words in the height table;\cr +|@!nd|&number of words in the depth table;\cr +|@!ni|&number of words in the italic correction table;\cr +|@!nl|&number of words in the lig/kern table;\cr +|@!nk|&number of words in the kern table;\cr +|@!ne|&number of words in the extensible character table;\cr +|@!np|&number of font parameter words.\cr}}$$ +They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|, +and +$$\hbox{|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|). When two or more 8-bit bytes are +combined to form an integer of 16 or more bits, the bytes appear in +BigEndian order. +@^BigEndian order@> + +@<Glob...@>= +@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..@'77777; + {subfile sizes} + +@ The rest of the \.{TFM} file may be regarded as a sequence of ten data +arrays having the informal specification +$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2} +\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr +header&|[0..lh-1]@t\\{stuff}@>|\cr +char\_info&|[bc..ec]char_info_word|\cr +width&|[0..nw-1]fix_word|\cr +height&|[0..nh-1]fix_word|\cr +depth&|[0..nd-1]fix_word|\cr +italic&|[0..ni-1]fix_word|\cr +lig\_kern&|[0..nl-1]lig_kern_command|\cr +kern&|[0..nk-1]fix_word|\cr +exten&|[0..ne-1]extensible_recipe|\cr +param&|[1..np]fix_word|\cr}}$$ +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 two of +the |fix_word| values must lie between $-16$ and $+16$. + +@ 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 \.{TFM} 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. + +\yskip\hang|header[0]| is a 32-bit check sum that \.{GFtoDVI} will copy into the +\.{DVI} output file whenever it uses the font. Later on when the \.{DVI} +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 +\.{TFM} file used by \.{GFtoDVI}. In this way, users will be warned about +potential incompatibilities. (However, if the check sum is zero in either +the font file or the \.{TFM} file, no check is made.) The actual relation +between this check sum and the rest of the \.{TFM} file is not important; +the check sum is simply an identification number with the property that +incompatible fonts almost always have distinct check sums. +@^check sum@> + +\yskip\hang|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 `\.{at} $\delta$ \.{pt}', the effect is to override the +design size and replace it by $\delta$, and to multiply the $x$ and~$y$ +coordinates of the points in the font image by a factor of $\delta$ +divided by the design size. Similarly, specific sizes can be substituted +for the design size by \.{GFtoDVI} commands like `\.{titlefontat}'. {\sl +All other dimensions in the\/\ \.{TFM} file are |fix_word| numbers in +design-size units.} Thus, for example, the value of |param[6]|, one \.{em} +or \.{\\quad}, 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 \.{TFM} file whose +first byte might be something besides 0 or 255. @^design size@>@^at size@> + +\yskip\hang|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 \.{ASCII} for standard +ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math +extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for +special-purpose non-alphabetic fonts, \.{GFGRAY} for \.{GFtoDVI}'s +gray fonts, \.{GFSLANT} for \.{GFtoDVI}'s slant fonts, \.{UNSPECIFIED} for +the default case when there is no information. Parentheses should not +appear in this name. (Such a string is said to be in {\mc BCPL} format.) +@^coding scheme@>@^gray fonts@>@^slant fonts@> + +\yskip\hang|header[12..@twhatever@>]| might also be present. + +@ 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. + +\yskip\hang first byte: |@!width_index| (8 bits)\par +\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index| + (4~bits)\par +\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag| + (2~bits)\par +\hang fourth byte: |@!remainder| (8 bits)\par +\yskip\noindent +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 \.{TFM} 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|. + +@ The |tag| field in a |char_info_word| has four values that explain how to +interpret the |remainder| field. + +\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par +\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning +program starting at |lig_kern[remainder]|.\par +\hang|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.\par +\hang|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]|.\par + +@d no_tag=0 {vanilla character} +@d lig_tag=1 {character has a ligature/kerning program} +@d list_tag=2 {character has a successor in a charlist} +@d ext_tag=3 {character is extensible} + +@ The |lig_kern| array contains instructions in a simple programming language +that explains what to do for special letter pairs. Each word in this array is a +|@!lig_kern_command| of four bytes. + +\yskip\hang 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.\par +\hang second byte: |next_char|, ``if |next_char| follows the current character, + then perform the operation and stop, otherwise continue.''\par +\hang third byte: |op_byte|, indicates a ligature step if less than~128, + a kern step otherwise.\par +\hang fourth byte: |remainder|.\par +\yskip\noindent +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\le a\le b+c$ and $0\le b,c\le1$. 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). + +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 +or kerning command is performed. + +@d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program} +@d kern_flag=128 {op code for a kern step} + +@ 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. + +@ The final portion of a \.{TFM} file is the |param| array, which is another +sequence of |fix_word| values. + +\yskip\hang|param[1]=@!slant| is the amount of italic slant. +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. + +\hang|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. + +\hang|param[3]=space_stretch| is the amount of glue stretching between words. + +\hang|param[4]=space_shrink| is the amount of glue shrinking between words. + +\hang|param[5]=x_height| is the height of letters for which accents don't +have to be raised or lowered. + +\hang|param[6]=quad| is the size of one em in the font. + +\hang|param[7]=extra_space| is the amount added to |param[2]| at the +ends of sentences. + +When the character coding scheme is \.{GFGRAY} or \.{GFSLANT}, the font is +supposed to contain an additional parameter called +|default_rule_thickness|. Other special parameters go with other coding +schemes. + +@* Input from binary files. +We have seen that \.{GF} and \.{DVI} and \.{TFM} files are sequences of +8-bit bytes. The bytes appear physically in what is called a `|packed +file of 0..255|' in \PASCAL\ lingo. + +Packing is system dependent, and many \PASCAL\ systems fail to implement +such files in a sensible way (at least, from the viewpoint of producing +good production software). For example, some systems treat all +byte-oriented files as text, looking for end-of-line marks and such +things. Therefore some system-dependent code is often needed to deal with +binary files, even though most of the program in this section of +\.{GFtoDVI} is written in standard \PASCAL. +@^system dependencies@> + +One common way to solve the problem is to consider files of |integer| +numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to +a sequence of four bytes $(a,b,c,d)$ using the following code, which +avoids the controversial integer division of negative numbers: +$$\vbox{\halign{#\hfil\cr +|if x>=0 then a:=x div @'100000000|\cr +|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr +\quad|end|;\cr +|x:=x mod @'100000000;|\cr +|b:=x div @'200000; x:=x mod @'200000;|\cr +|c:=x div @'400; d:=x mod @'400;|\cr}}$$ +The four bytes are then kept in a buffer and output one by one. (On 36-bit +computers, an additional division by 16 is necessary at the beginning. +Another way to separate an integer into four bytes is to use/abuse +\PASCAL's variant records, storing an integer and retrieving bytes that are +packed in the same place; {\sl caveat implementor!\/}) It is also desirable +in some cases to read a hundred or so integers at a time, maintaining a +larger buffer. + +We shall stick to simple \PASCAL\ in this program, for reasons of clarity, +even if such simplicity is sometimes unrealistic. + +@<Types ...@>= +@!eight_bits=0..255; {unsigned one-byte quantity} +@!byte_file=packed file of eight_bits; {files that contain binary data} + +@ The program deals with three binary file variables: |gf_file| is the main +input file that we are converting into a document; |dvi_file| is the main +output file that will specify that document; and |tfm_file| is +the current font metric file from which character-width information is +being read. + +@<Glob...@>= +@!gf_file:byte_file; {the character data we are reading} +@!dvi_file:byte_file; {the typesetting instructions we are writing} +@!tfm_file:byte_file; {a font metric file} + +@ To prepare these files for input or output, we |reset| or |rewrite| +them. An extension of \PASCAL\ is needed, since we want to associate +it with external files whose names are specified dynamically (i.e., not +known at compile time). The following code assumes that `|reset(f,s)|' and +`|rewrite(f,s)|' do this, when |f| is a file variable and |s| is a string +variable that specifies the file name. +@^system dependencies@> + +@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|} +begin reset(gf_file,name_of_file); +cur_loc:=0; +end; +@# +procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|} +begin reset(tfm_file,name_of_file); +end; +@# +procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|} +begin rewrite(dvi_file,name_of_file); +end; + +@ If you looked carefully at the preceding code, you probably asked, +``What are |cur_loc| and |name_of_file|?'' Good question. They are global +variables: The integer |cur_loc| tells which byte of the input file will +be read next, and the string |name_of_file| will be set to the current +file name before the file-opening procedures are called. + +@<Glob...@>= +@!cur_loc:integer; {current byte number in |gf_file|} +@!name_of_file:packed array[1..file_name_size] of char; {external file name} + +@ It turns out to be convenient to read four bytes at a time, when we are +inputting from \.{TFM} files. The input goes into global variables +|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3| +the fourth. + +@<Glob...@>= +@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once} + +@ The |read_tfm_word| procedure sets |b0| through |b3| to the next +four bytes in the current \.{TFM} file. +@^system dependencies@> + +@p procedure read_tfm_word; +begin read(tfm_file,b0); read(tfm_file,b1); +read(tfm_file,b2); read(tfm_file,b3); +end; + +@ We shall use another set of simple functions to read the next byte or +bytes from |gf_file|. There are four possibilities, each of which is +treated as a separate function in order to minimize the overhead for +subroutine calls. +@^system dependencies@> + +@p function get_byte:integer; {returns the next byte, unsigned} +var b:eight_bits; +begin if eof(gf_file) then get_byte:=0 +else begin read(gf_file,b); incr(cur_loc); get_byte:=b; + end; +end; +@# +function get_two_bytes:integer; {returns the next two bytes, unsigned} +var a,@!b:eight_bits; +begin read(gf_file,a); read(gf_file,b); +cur_loc:=cur_loc+2; +get_two_bytes:=a*256+b; +end; +@# +function get_three_bytes:integer; {returns the next three bytes, unsigned} +var a,@!b,@!c:eight_bits; +begin read(gf_file,a); read(gf_file,b); read(gf_file,c); +cur_loc:=cur_loc+3; +get_three_bytes:=(a*256+b)*256+c; +end; +@# +function signed_quad:integer; {returns the next four bytes, signed} +var a,@!b,@!c,@!d:eight_bits; +begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d); +cur_loc:=cur_loc+4; +if a<128 then signed_quad:=((a*256+b)*256+c)*256+d +else signed_quad:=(((a-256)*256+b)*256+c)*256+d; +end; + +@* Reading the font information. +Now let's get down to brass tacks and consider the more substantial +routines that actually convert \.{TFM} data into a form suitable for +computation. The routines in this part of the program have been borrowed +from \TeX, with slight changes, since \.{GFtoDVI} has to do some of the +things that \TeX\ does. + +The \.{TFM} data is stored in a large array called +|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word| +data gets converted into |scaled| entries, while everything else goes into +words of type |four_quarters|. (These data structures are special cases of +the more general memory words of \TeX. On some machines it is necessary to +define |min_quarterword=-128| and |max_quarterword=127| in order to pack +four quarterwords into a single word.) +@^system dependencies@> + +@d min_quarterword=0 {change this to allow efficient packing, if necessary} +@d max_quarterword=255 {ditto} +@d qi(#)==#+min_quarterword + {to put an |eight_bits| item into a quarterword} +@d qo(#)==#-min_quarterword + {to take an |eight_bits| item out of a quarterword} +@d title_font=1 +@d label_font=2 +@d gray_font=3 +@d slant_font=4 +@d logo_font=5 +@d non_char==qi(256) +@d non_address==font_mem_size + +@<Types ...@>= +@!font_index = 0..font_mem_size; +@!quarterword = min_quarterword..max_quarterword; {1/4 of a word} +@!four_quarters = packed record@;@/ + @!b0:quarterword; + @!b1:quarterword; + @!b2:quarterword; + @!b3:quarterword; + end; +@!memory_word = record@;@/ + case boolean of + true: (@!sc:scaled); + false: (@!qqqq:four_quarters); + end; +@!internal_font_number=title_font..logo_font; + +@ Besides |font_info|, there are also a number of index arrays that point +into it, so that we can locate width and height information, etc. For +example, the |char_info| data for character |c| in font |f| will be in +|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index| part of +this word (the |b0| field), the width of the character is +|font_info[width_base[f]+w].sc|. (These formulas assume that +|min_quarterword| has already been added to |w|, but not to |c|.) + +@<Glob...@>= +@!font_info:array[font_index] of memory_word; {the font metric data} +@!fmem_ptr:font_index; {first unused word of |font_info|} +@!font_check:array[internal_font_number] of four_quarters; {check sum} +@!font_size:array[internal_font_number] of scaled; {``at'' size} +@!font_dsize:array[internal_font_number] of scaled; {``design'' size} +@!font_bc:array[internal_font_number] of eight_bits; + {beginning (smallest) character code} +@!font_ec:array[internal_font_number] of eight_bits; + {ending (largest) character code} +@!char_base:array[internal_font_number] of integer; + {base addresses for |char_info|} +@!width_base:array[internal_font_number] of integer; + {base addresses for widths} +@!height_base:array[internal_font_number] of integer; + {base addresses for heights} +@!depth_base:array[internal_font_number] of integer; + {base addresses for depths} +@!italic_base:array[internal_font_number] of integer; + {base addresses for italic corrections} +@!lig_kern_base:array[internal_font_number] of integer; + {base addresses for ligature/kerning programs} +@!kern_base:array[internal_font_number] of integer; + {base addresses for kerns} +@!exten_base:array[internal_font_number] of integer; + {base addresses for extensible recipes} +@!param_base:array[internal_font_number] of integer; + {base addresses for font parameters} +@!bchar_label:array[internal_font_number] of font_index; + {start of |lig_kern| program for left boundary character, + |non_address| if there is none} +@!font_bchar:array[internal_font_number] of min_quarterword..non_char; + {right boundary character, |non_char| if there is none} + +@ @<Set init...@>= +fmem_ptr:=0; + +@ Of course we want to define macros that suppress the detail of how font +information is actually packed, so that we don't have to write things like +$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$ +too often. The \.{WEB} definitions here make |char_info(f)(c)| the +|four_quarters| word of font information corresponding to character +|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be +the character's width; hence the long formula above is at least +abbreviated to +$$\hbox{|char_width(f)(char_info(f)(c))|.}$$ +In practice we will try to fetch |q| first and look at several of its +fields at the same time. + +The italic correction of a character will be denoted by +|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get +at the height and depth in a slightly different way, since we usually want +to compute both height and depth if we want either one. The value of +|height_depth(q)| will be the 8-bit quantity +$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we +will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and +depth of the character |c| for which |q=char_info(f)(c)|. Got that? + +The tag field will be called |char_tag(q)|; and the remainder byte will be +called |rem_byte(q)|. + +@d char_info_end(#)==#].qqqq +@d char_info(#)==font_info[char_base[#]+char_info_end +@d char_width_end(#)==#.b0].sc +@d char_width(#)==font_info[width_base[#]+char_width_end +@d char_exists(#)==(#.b0>min_quarterword) +@d char_italic_end(#)==(qo(#.b2)) div 4].sc +@d char_italic(#)==font_info[italic_base[#]+char_italic_end +@d height_depth(#)==qo(#.b1) +@d char_height_end(#)==(#) div 16].sc +@d char_height(#)==font_info[height_base[#]+char_height_end +@d char_depth_end(#)==# mod 16].sc +@d char_depth(#)==font_info[depth_base[#]+char_depth_end +@d char_tag(#)==((qo(#.b2)) mod 4) +@d skip_byte(#)==qo(#.b0) +@d next_char(#)==#.b1 +@d op_byte(#)==qo(#.b2) +@d rem_byte(#)==#.b3 + +@ Here are some macros that help process ligatures and kerns. +We write |char_kern(f)(j)| to find the amount of kerning specified by +kerning command~|j| in font~|f|. + +@d lig_kern_start(#)==lig_kern_base[#]+rem_byte {beginning of lig/kern program} +@d lig_kern_restart_end(#)==256*(op_byte(#))+rem_byte(#) +@d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end +@d char_kern_end(#)==256*(op_byte(#)-128)+rem_byte(#)].sc +@d char_kern(#)==font_info[kern_base[#]+char_kern_end + +@ Font parameters are referred to as |slant(f)|, |space(f)|, etc. + +@d param_end(#)==param_base[#]].sc +@d param(#)==font_info[#+param_end +@d slant==param(1) {slant to the right, per unit distance upward} +@d space==param(2) {normal space between words} +@d x_height==param(5) {one ex} +@d default_rule_thickness==param(8) {thickness of rules} + +@ Here is the subroutine that inputs the information on |tfm_file|, assuming +that the file has just been reset. Parameter~|f| tells which metric file is +being read (either |title_font| or |label_font| or |gray_font| or |slant_font| +or |logo_font|); parameter~|s| is the ``at'' size, which will be +substituted for the design size if it is positive. + +This routine does only limited checking of the validity of the file, +because another program (\.{TFtoPL}) is available to diagnose errors in +the rare case that something is amiss. + +@d bad_tfm=11 {label for |read_font_info|} +@d abend==goto bad_tfm {do this when the \.{TFM} data is wrong} + +@p procedure read_font_info(@!f:integer;@!s:scaled); {input a \.{TFM} file} +label done,bad_tfm; +var k:font_index; {index into |font_info|} +@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..65535; + {sizes of subfiles} +@!bch_label:integer; {left boundary label for ligatures} +@!bchar:0..256; {right boundary character for ligatures} +@!qw:four_quarters;@!sw:scaled; {accumulators} +@!z:scaled; {the design size or the ``at'' size} +@!alpha:integer;@!beta:1..16; + {auxiliary quantities used in fixed-point multiplication} +begin @<Read and check the font data; |abend| if the \.{TFM} file is + malformed; otherwise |goto done|@>; +bad_tfm: print_nl('Bad TFM file for'); +@.Bad TFM file...@> +case f of +title_font:abort('titles!'); +label_font:abort('labels!'); +gray_font:abort('pixels!'); +slant_font:abort('slants!'); +logo_font:abort('METAFONT logo!'); +end; {there are no other cases} +done: {it might be good to close |tfm_file| now} +end; + +@ @<Read and check...@>= +@<Read the {\.{TFM}} size fields@>; +@<Use size fields to allocate font information@>; +@<Read the {\.{TFM}} header@>; +@<Read character data@>; +@<Read box dimensions@>; +@<Read ligature/kern program@>; +@<Read extensible character recipes@>; +@<Read font parameters@>; +@<Make final adjustments and |goto done|@> + +@ @d read_two_halves_end(#)==#:=b2*256+b3 +@d read_two_halves(#)==read_tfm_word; #:=b0*256+b1; read_two_halves_end + +@<Read the {\.{TFM}} size fields@>= +begin read_two_halves(lf)(lh); +read_two_halves(bc)(ec); +if (bc>ec+1)or(ec>255) then abend; +if bc>255 then {|bc=256| and |ec=255|} + begin bc:=1; ec:=0; + end; +read_two_halves(nw)(nh); +read_two_halves(nd)(ni); +read_two_halves(nl)(nk); +read_two_halves(ne)(np); +if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abend; +end + +@ The preliminary settings of the index variables |width_base|, +|lig_kern_base|, |kern_base|, and |exten_base| will be corrected later by +subtracting |min_quarterword| from them; and we will subtract 1 from +|param_base| too. It's best to forget about such anomalies until later. + +@<Use size fields to allocate font information@>= +lf:=lf-6-lh; {|lf| words should be loaded into |font_info|} +if np<8 then lf:=lf+8-np; {at least eight parameters will appear} +if fmem_ptr+lf>font_mem_size then abort('No room for TFM file!'); +@.No room for TFM file@> +char_base[f]:=fmem_ptr-bc; +width_base[f]:=char_base[f]+ec+1; +height_base[f]:=width_base[f]+nw; +depth_base[f]:=height_base[f]+nh; +italic_base[f]:=depth_base[f]+nd; +lig_kern_base[f]:=italic_base[f]+ni; +kern_base[f]:=lig_kern_base[f]+nl; +exten_base[f]:=kern_base[f]+nk; +param_base[f]:=exten_base[f]+ne + +@ Only the first two words of the header are needed by \.{GFtoDVI}. + +@d store_four_quarters(#)== + begin read_tfm_word; + qw.b0:=qi(b0); qw.b1:=qi(b1); qw.b2:=qi(b2); qw.b3:=qi(b3); + #:=qw; + end + +@<Read the {\.{TFM}} header@>= +begin if lh<2 then abend; +store_four_quarters(font_check[f]); +read_tfm_word; +if b0>127 then abend; {design size must be positive} +z:=((b0*256+b1)*256+b2)*16+(b3 div 16); +if z<unity then abend; +while lh>2 do + begin read_tfm_word; decr(lh); {ignore the rest of the header} + end; +font_dsize[f]:=z; +if s>0 then z:=s; +font_size[f]:=z; +end + +@ @<Read character data@>= +for k:=fmem_ptr to width_base[f]-1 do + begin store_four_quarters(font_info[k].qqqq); + if (b0>=nw)or(b1 div @'20>=nh)or(b1 mod @'20>=nd)or + (b2 div 4>=ni) then abend; + case b2 mod 4 of + lig_tag: if b3>=nl then abend; + ext_tag: if b3>=ne then abend; + no_tag,list_tag: do_nothing; + end; {there are no other cases} + end + +@ A |fix_word| whose four bytes are $(b0,b1,b2,b3)$ from left to right +represents the number +$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr +b_1\cdot2^{-4}+b_2\cdot2^{-12}+b_3\cdot2^{-20}&b_0=0;\cr +-16+b_1\cdot2^{-4}+b_2\cdot2^{-12}+b_3\cdot2^{-20}&b_0=255.\cr}}\right.$$ +(No other choices of |b0| are allowed, since the magnitude of a number in +design-size units must be less than 16.) We want to multiply this +quantity by the integer~|z|, which is known to be less than $2^{27}$. Let +$\alpha=16z$. If $|z|<2^{23}$, the individual multiplications $b\cdot z$, +$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, +4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can +compensate for this later. If |z| has thereby been replaced by +$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute +$$\lfloor(b_1+b_2\cdot2^{-8}+b_3\cdot2^{-16})\,z^\prime/\beta\rfloor$$ +if $a=0$, or the same quantity minus $\alpha$ if $a=255$. + +@d store_scaled(#)==begin read_tfm_word; + sw:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta; + if b0=0 then #:=sw@+else if b0=255 then #:=sw-alpha@+else abend; + end + +@<Read box dimensions@>= +begin @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; +for k:=width_base[f] to lig_kern_base[f]-1 do + store_scaled(font_info[k].sc); +if font_info[width_base[f]].sc<>0 then abend; {\\{width}[0] must be zero} +if font_info[height_base[f]].sc<>0 then abend; {\\{height}[0] must be zero} +if font_info[depth_base[f]].sc<>0 then abend; {\\{depth}[0] must be zero} +if font_info[italic_base[f]].sc<>0 then abend; {\\{italic}[0] must be zero} +end + +@ @<Replace |z|...@>= +begin alpha:=16*z; beta:=16; +while z>=@'40000000 do + begin z:=z div 2; beta:=beta div 2; + end; +end + +@ @d check_byte_range(#)==begin if (#<bc)or(#>ec) then abend@+end + +@<Read ligature/kern program@>= +begin bch_label:=@'77777; bchar:=256; +if nl>0 then + begin for k:=lig_kern_base[f] to kern_base[f]-1 do + begin store_four_quarters(font_info[k].qqqq); + if b0>stop_flag then + begin if 256*b2+b3>=nl then abend; + if b0=255 then if k=lig_kern_base[f] then bchar:=b1; + end + else begin if b1<>bchar then check_byte_range(b1); + if b2<kern_flag then check_byte_range(b3) + else if 256*(b2-128)+b3>=nk then abend; + end; + end; + if b0=255 then bch_label:=256*b2+b3; + end; +for k:=kern_base[f] to exten_base[f]-1 do + store_scaled(font_info[k].sc); +end + +@ @<Read extensible character recipes@>= +for k:=exten_base[f] to param_base[f]-1 do + begin store_four_quarters(font_info[k].qqqq); + if b0<>0 then check_byte_range(b0); + if b1<>0 then check_byte_range(b1); + if b2<>0 then check_byte_range(b2); + check_byte_range(b3); + end + +@ @<Read font parameters@>= +begin for k:=1 to np do + if k=1 then {the |slant| parameter is a pure number} + begin read_tfm_word; + if b0>127 then sw:=b0-256@+else sw:=b0; + sw:=sw*@'400+b1; sw:=sw*@'400+b2; + font_info[param_base[f]].sc:=(sw*@'20)+(b3 div@'20); + end + else store_scaled(font_info[param_base[f]+k-1].sc); +for k:=np+1 to 8 do font_info[param_base[f]+k-1].sc:=0; +end + +@ Now to wrap it up, we have checked all the necessary things about the \.{TFM} +file, and all we need to do is put the finishing touches on the data for +the new font. + +@d adjust(#)==#[f]:=qo(#[f]) + {correct for the excess |min_quarterword| that was added} + +@<Make final adjustments...@>= +font_bc[f]:=bc; font_ec[f]:=ec; +if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f] +else bchar_label[f]:=non_address; +font_bchar[f]:=qi(bchar); +adjust(width_base); adjust(lig_kern_base); +adjust(kern_base); adjust(exten_base); +decr(param_base[f]); +fmem_ptr:=fmem_ptr+lf; goto done + +@* The string pool. +\.{GFtoDVI} remembers strings by putting them into an array called +|str_pool|. The |str_start| array tells where each string starts in the pool. + +@<Types ...@>= +@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|} +@!str_number = 0..max_strings; {for variables that point into |str_start|} + +@ As new strings enter, we keep track of the storage currently used, by +means of two global variables called |pool_ptr| and |str_ptr|. These are +periodically reset to their initial values when we move from one character +to another, because most strings are of only temporary interest. + +@<Glob...@>= +@!str_pool:packed array[pool_pointer] of ASCII_code; {the characters} +@!str_start : array[str_number] of pool_pointer; {the starting pointers} +@!pool_ptr : pool_pointer; {first unused position in |str_pool|} +@!str_ptr : str_number; {start of the current string being created} +@!init_str_ptr:str_number; {|str_ptr| setting when a new character starts} + +@ Several of the elementary string operations are performed using \.{WEB} +macros instead of using \PASCAL\ procedures, because many of the +operations are done quite frequently and we want to avoid the +overhead of procedure calls. For example, here is +a simple macro that computes the length of a string. +@.WEB@> + +@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters + in string number \#} + +@ Strings are created by appending character codes to |str_pool|. +The macro called |append_char|, defined here, does not check to see if the +value of |pool_ptr| has gotten too high; that test is supposed to be +made before |append_char| is used. + +To test if there is room to append |l| more characters to |str_pool|, +we shall write |str_room(l)|, which aborts \.{GFtoDVI} and gives an +apologetic error message if there isn't enough room. + +@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|} +begin str_pool[pool_ptr]:=#; incr(pool_ptr); +end +@d str_room(#) == {make sure that the pool hasn't overflowed} + begin if pool_ptr+# > pool_size then + abort('Too many strings!'); +@.Too many strings@> + end + +@ Once a sequence of characters has been appended to |str_pool|, it +officially becomes a string when the function |make_string| is called. +This function returns the identification number of the new string as its +value. + +@p function make_string : str_number; {current string enters the pool} +begin if str_ptr=max_strings then + abort('Too many labels!'); +@.Too many labels@> +incr(str_ptr); str_start[str_ptr]:=pool_ptr; +make_string:=str_ptr-1; +end; + +@ The first strings in the string pool are the keywords that \.{GFtoDVI} +recognizes in the \\{xxx} commands of a \.{GF} file. They are entered +into |str_pool| by means of a tedious bunch of assignment statements, +together with calls on the |first_string| subroutine. + +@d init_str0(#)==first_string(#) +@d init_str1(#)==buffer[1]:=#; init_str0 +@d init_str2(#)==buffer[2]:=#; init_str1 +@d init_str3(#)==buffer[3]:=#; init_str2 +@d init_str4(#)==buffer[4]:=#; init_str3 +@d init_str5(#)==buffer[5]:=#; init_str4 +@d init_str6(#)==buffer[6]:=#; init_str5 +@d init_str7(#)==buffer[7]:=#; init_str6 +@d init_str8(#)==buffer[8]:=#; init_str7 +@d init_str9(#)==buffer[9]:=#; init_str8 +@d init_str10(#)==buffer[10]:=#; init_str9 +@d init_str11(#)==buffer[11]:=#; init_str10 +@d init_str12(#)==buffer[12]:=#; init_str11 +@d init_str13(#)==buffer[13]:=#; init_str12 +@d longest_keyword=13 + +@p procedure first_string(@!c:integer); +begin if str_ptr<>c then abort('?'); {internal consistency check} +@.?@> +while l>0 do + begin append_char(buffer[l]); decr(l); + end; +incr(str_ptr); str_start[str_ptr]:=pool_ptr; +end; + +@ @<Glob...@>= +@!l:integer; {length of string being made by |first_string|} + +@ Here are the tedious assignments just promised. +String number 0 is the empty string. + +@d null_string=0 {the empty keyword} +@d area_code=4 {add to font code for the `\.{area}' keywords} +@d at_code=8 {add to font code for the `\.{at}' keywords} +@d rule_code=13 {code for the keyword `\.{rule}'} +@d title_code=14 {code for the keyword `\.{title}'} +@d rule_thickness_code=15 {code for the keyword `\.{rulethickness}'} +@d offset_code=16 {code for the keyword `\.{offset}'} +@d x_offset_code=17 {code for the keyword `\.{xoffset}'} +@d y_offset_code=18 {code for the keyword `\.{yoffset}'} +@d max_keyword=18 {largest keyword code number} + +@<Initialize the strings@>= +str_ptr:=0; pool_ptr:=0; str_start[0]:=0;@/ +l:=0; init_str0(null_string);@/ +l:=9; init_str9("t")("i")("t")("l")("e")("f")("o")("n")("t")(title_font);@/ +l:=9; init_str9("l")("a")("b")("e")("l")("f")("o")("n")("t")(label_font);@/ +l:=8; init_str8("g")("r")("a")("y")("f")("o")("n")("t")(gray_font);@/ +l:=9; init_str9("s")("l")("a")("n")("t")("f")("o")("n")("t")(slant_font);@/ +l:=13; init_str13("t")("i")("t")("l")("e") + ("f")("o")("n")("t")("a")("r")("e")("a")(title_font+area_code);@/ +l:=13; init_str13("l")("a")("b")("e")("l") + ("f")("o")("n")("t")("a")("r")("e")("a")(label_font+area_code);@/ +l:=12; init_str12("g")("r")("a")("y") + ("f")("o")("n")("t")("a")("r")("e")("a")(gray_font+area_code);@/ +l:=13; init_str13("s")("l")("a")("n")("t") + ("f")("o")("n")("t")("a")("r")("e")("a")(slant_font+area_code);@/ +l:=11; init_str11("t")("i")("t")("l")("e") + ("f")("o")("n")("t")("a")("t")(title_font+at_code);@/ +l:=11; init_str11("l")("a")("b")("e")("l") + ("f")("o")("n")("t")("a")("t")(label_font+at_code);@/ +l:=10; init_str10("g")("r")("a")("y") + ("f")("o")("n")("t")("a")("t")(gray_font+at_code);@/ +l:=11; init_str11("s")("l")("a")("n")("t") + ("f")("o")("n")("t")("a")("t")(slant_font+at_code);@/ +l:=4; init_str4("r")("u")("l")("e")(rule_code);@/ +l:=5; init_str5("t")("i")("t")("l")("e")(title_code);@/ +l:=13; init_str13("r")("u")("l")("e") + ("t")("h")("i")("c")("k")("n")("e")("s")("s")(rule_thickness_code);@/ +l:=6; init_str6("o")("f")("f")("s")("e")("t")(offset_code);@/ +l:=7; init_str7("x")("o")("f")("f")("s")("e")("t")(x_offset_code);@/ +l:=7; init_str7("y")("o")("f")("f")("s")("e")("t")(y_offset_code);@/ + +@ We will also find it useful to have the following strings. (The names of +default fonts will presumably be different at different sites.) +@^system dependencies@> +@^default fonts@> + +@d gf_ext=max_keyword+1 {string number for `\.{.gf}'} +@d dvi_ext=max_keyword+2 {string number for `\.{.dvi}'} +@d tfm_ext=max_keyword+3 {string number for `\.{.tfm}'} +@d page_header=max_keyword+4 {string number for `\.{\ \ Page\ }'} +@d char_header=max_keyword+5 {string number for `\.{\ \ Character\ }'} +@d ext_header=max_keyword+6 {string number for `\.{\ \ Ext\ }'} +@d left_quotes=max_keyword+7 {string number for `\.{\ \ ``}'} +@d right_quotes=max_keyword+8 {string number for `\.{''}'} +@d equals_sign=max_keyword+9 {string number for `\.{ = }'} +@d plus_sign=max_keyword+10 {string number for `\.{ + (}'} +@d default_title_font=max_keyword+11 + {string number for the default |title_font|} +@d default_label_font=max_keyword+12 + {string number for the default |label_font|} +@d default_gray_font=max_keyword+13 {string number for the default |gray_font|} +@d logo_font_name=max_keyword+14 {string number for the font with \MF\ logo} +@d small_logo=max_keyword+15 {string number for `\.{METAFONT}'} +@d home_font_area=max_keyword+16 {string number for system-dependent font area} + +@<Initialize the strings@>= +l:=3; init_str3(".")("g")("f")(gf_ext);@/ +l:=4; init_str4(".")("d")("v")("i")(dvi_ext);@/ +l:=4; init_str4(".")("t")("f")("m")(tfm_ext);@/ +l:=7; init_str7(" ")(" ")("P")("a")("g")("e")(" ")(page_header);@/ +l:=12; init_str12(" ")(" ")("C")("h")("a")("r")("a")("c")("t")("e")("r")(" ") + (char_header);@/ +l:=6; init_str6(" ")(" ")("E")("x")("t")(" ")(ext_header);@/ +l:=4; init_str4(" ")(" ")("`")("`")(left_quotes);@/ +l:=2; init_str2("'")("'")(right_quotes);@/ +l:=3; init_str3(" ")("=")(" ")(equals_sign);@/ +l:=4; init_str4(" ")("+")(" ")("(")(plus_sign);@/ +l:=4; init_str4("c")("m")("r")("8")(default_title_font);@/ +l:=6; init_str6("c")("m")("t")("t")("1")("0")(default_label_font);@/ +l:=4; init_str4("g")("r")("a")("y")(default_gray_font);@/ +l:=5; init_str5("l")("o")("g")("o")("8")(logo_font_name);@/ +l:=8; init_str8("M")("E")("T")("A")("F")("O")("N")("T")(small_logo); + +@ If an \\{xxx} command has just been encountered in the \.{GF} file, +the following procedure interprets its keyword. More precisely, we assume +that |cur_gf| contains an op-code byte just read from the \.{GF} file, +where |xxx1<=cur_gf<=no_op|. The |interpret_xxx| procedure will read the +rest of the command, in the following way: +\smallskip +\item{1)} If |cur_gf| is |no_op| or |yyy|, or if it's an \\{xxx} command with +an unknown keyword, the bytes are simply read and ignored, and the +value |no_operation| is returned. + +\item{2)} If |cur_gf| is an \\{xxx} command (either |xxx1| or $\cdots$ +or |xxx4|), and if the associated string matches a keyword exactly, +the string number of that keyword is returned (e.g., |rule_thickness_code|). + +\item{3)} If |cur_gf| is an \\{xxx} command whose string begins with +keyword and space, the string number of that keyword is returned, and +the remainder of the string is put into the string pool (where it will be +string number |cur_string|. Exception: If the keyword is |null_string|, +the character immediately following the blank space is put into the +global variable |label_type|, and the remaining characters go into the +string pool. + +\smallskip\noindent +In all cases, |cur_gf| will then be reset to the op-code byte that +immediately follows the original command. + +@d no_operation=max_keyword+1 + +@<Types ...@>= +@!keyword_code=null_string..no_operation; + +@ @<Glob...@>= +@!cur_gf:eight_bits; {the byte most recently read from |gf_file|} +@!cur_string:str_number; {the string following a keyword and space} +@!label_type:eight_bits; {the character following a null keyword and space} + +@ We will be using this procedure when reading the \.{GF} file just +after the preamble and just after |eoc| commands. + +@p function interpret_xxx:keyword_code; +label done,done1,not_found; +var @!k:integer; {number of bytes in an \\{xxx} command} +@!j:integer; {number of bytes read so far} +@!l:0..longest_keyword; {length of keyword to check} +@!m:keyword_code; {runs through the list of known keywords} +@!n1:0..longest_keyword; {buffered character being checked} +@!n2:pool_pointer; {pool character being checked} +@!c:keyword_code; {the result to return} +begin c:=no_operation; cur_string:=null_string; +case cur_gf of +no_op:goto done; +yyy:begin k:=signed_quad; goto done; + end; +xxx1:k:=get_byte; +xxx2:k:=get_two_bytes; +xxx3:k:=get_three_bytes; +xxx4:k:=signed_quad; +end; {there are no other cases} +@<Read the next |k| characters of the \.{GF} file; + change |c| and |goto done| if a keyword is recognized@>; +done: cur_gf:=get_byte; interpret_xxx:=c; +end; + +@ @<Read the next |k|...@>= +j:=0;@+if k<2 then goto not_found; +loop@+ begin l:=j; + if j=k then goto done1; + if j=longest_keyword then goto not_found; + incr(j); buffer[j]:=get_byte; + if buffer[j]=" " then goto done1; + end; +done1:@<If the keyword in |buffer[1..l]| is known, change |c| and |goto done|@>; +not_found: while j<k do + begin incr(j); cur_gf:=get_byte; + end + +@ @<If the keyword...@>= +for m:=null_string to max_keyword do if length(m)=l then + begin n1:=0; n2:=str_start[m]; + while (n1<l)and(buffer[n1+1]=str_pool[n2]) do + begin incr(n1); incr(n2); + end; + if n1=l then + begin c:=m; + if m=null_string then + begin incr(j); label_type:=get_byte; + end; + str_room(k-j); + while j<k do + begin incr(j); append_char(get_byte); + end; + cur_string:=make_string; goto done; + end; + end + +@ When an \\{xxx} command takes a numeric argument, |get_yyy| reads +that argument and puts the following byte into |cur_gf|. + +@p function get_yyy:scaled; +var @!v:scaled; {value just read} +begin if cur_gf<>yyy then get_yyy:=0 +else begin v:=signed_quad; cur_gf:=get_byte; get_yyy:=v; + end; +end; + +@ A simpler method is used for special commands between |boc| and |eoc|, +since \.{GFtoDVI} doesn't even look at them. + +@p procedure skip_nop; +label done; +var @!k:integer; {number of bytes in an \\{xxx} command} +@!j:integer; {number of bytes read so far} +begin case cur_gf of +no_op:goto done; +yyy:begin k:=signed_quad; goto done; + end; +xxx1:k:=get_byte; +xxx2:k:=get_two_bytes; +xxx3:k:=get_three_bytes; +xxx4:k:=signed_quad; +end; {there are no other cases} +for j:=1 to k do cur_gf:=get_byte; +done: cur_gf:=get_byte; +end; + +@* File names. +It's time now to fret about file names. \.{GFtoDVI} uses the conventions of +\TeX\ and \MF\ to convert file names into strings that can be used to open +files. Three routines called |begin_name|, |more_name|, and |end_name| are +involved, so that the system-dependent parts of file naming conventions are +isolated from the system-independent ways in which file names are used. +(See the \TeX\ or \MF\ program listing for further explanation.) +@^system dependencies@> + +@<Glob...@>= +@!cur_name:str_number; {name of file just scanned} +@!cur_area:str_number; {file area just scanned, or |null_string|} +@!cur_ext:str_number; {file extension just scanned, or |null_string|} + +@ The file names we shall deal with for illustrative purposes have the +following structure: If the name contains `\.>' or `\.:', the file area +consists of all characters up to and including the final such character; +otherwise the file area is null. If the remaining file name contains +`\..', the file extension consists of all such characters from the first +remaining `\..' to the end, otherwise the file extension is null. +@^system dependencies@> + +We can scan such file names easily by using two global variables that keep track +of the occurrences of area and extension delimiters: + +@<Glob...@>= +@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any} +@!ext_delimiter:pool_pointer; {the relevant `\..', if any} + +@ Font metric files whose areas are not given +explicitly are assumed to appear in a standard system area called +|home_font_area|. This system area name will, of course, vary from place +to place. The program here sets it to `\.{TeXfonts:}'. +@^system dependencies@> +@.TeXfonts@> + +@<Initialize the strings@>= +l:=9; init_str9("T")("e")("X")("f")("o")("n")("t")("s")(":")(home_font_area);@/ + +@ Here now is the first of the system-dependent routines for file name scanning. +@^system dependencies@> + +@p procedure begin_name; +begin area_delimiter:=0; ext_delimiter:=0; +end; + +@ And here's the second. +@^system dependencies@> + +@p function more_name(@!c:ASCII_code):boolean; +begin if c=" " then more_name:=false +else begin if (c=">")or(c=":") then + begin area_delimiter:=pool_ptr; ext_delimiter:=0; + end + else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr; + str_room(1); append_char(c); {contribute |c| to the current string} + more_name:=true; + end; +end; + +@ The third. +@^system dependencies@> + +@p procedure end_name; +begin if str_ptr+3>max_strings then + abort('Too many strings!'); +@.Too many strings@> +if area_delimiter=0 then cur_area:=null_string +else begin cur_area:=str_ptr; incr(str_ptr); + str_start[str_ptr]:=area_delimiter+1; + end; +if ext_delimiter=0 then + begin cur_ext:=null_string; cur_name:=make_string; + end +else begin cur_name:=str_ptr; incr(str_ptr); + str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string; + end; +end; + +@ Another system-dependent routine is needed to convert three strings +into the |name_of_file| value that is used to open files. The present code +allows both lowercase and uppercase letters in the file name. +@^system dependencies@> + +@d append_to_name(#)==begin c:=#; incr(k); + if k<=file_name_size then name_of_file[k]:=xchr[c]; + end + +@p procedure pack_file_name(@!n,@!a,@!e:str_number); +var k:integer; {number of positions filled in |name_of_file|} +@!c: ASCII_code; {character being packed} +@!j:integer; {index into |str_pool|} +@!name_length:0..file_name_size; {number of characters packed} +begin k:=0; +for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]); +for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]); +for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]); +if k<=file_name_size then name_length:=k@+else name_length:=file_name_size; +for k:=name_length+1 to file_name_size do name_of_file[k]:=' '; +end; + +@ Now let's consider the routines by which \.{GFtoDVI} deals with file names +in a system-independent manner. +The global variable |job_name| contains the \.{GF} file name that is +being input. This name is extended by `\.{dvi}' +in order to make the name of the output file. + +@<Glob...@>= +@!job_name:str_number; {principal file name} + +@ The |start_gf| procedure prompts the user for the name of the generic +font file to be input. It opens the file, making sure that some input is +present; then it opens the output file. + +Although this routine is system-independent, it should probably be +modified to take the file name from the command line (without an initial +prompt), on systems that permit such things. + +@p procedure start_gf; +label found,done; +begin loop@+begin print_nl('GF file name: '); input_ln; +@.GF file name@> + buf_ptr:=0; buffer[line_length]:="?"; + while buffer[buf_ptr]=" " do incr(buf_ptr); + if buf_ptr<line_length then + begin @<Scan the file name in the buffer@>; + if cur_ext=null_string then cur_ext:=gf_ext; + pack_file_name(cur_name,cur_area,cur_ext); open_gf_file; + if not eof(gf_file) then goto found; + print_nl('Oops... I can''t find file '); print(name_of_file); +@.Oops...@> +@.I can't find...@> + end; + end; +found:job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext); +open_dvi_file; +end; + +@ @<Scan the file name in the buffer@>= +if buffer[line_length-1]="/" then + begin interaction:=true; decr(line_length); + end; +begin_name; +loop@+ begin if buf_ptr=line_length then goto done; + if not more_name(buffer[buf_ptr]) then goto done; + incr(buf_ptr); + end; +done:end_name + +@ Special instructions found near the beginning of the \.{GF} file might +change the names, areas, and ``at'' sizes of the fonts that \.{GFtoDVI} +will be using. But when we reach the first |boc| instruction, we input +all of the \.{TFM} files. The global variable |interaction| is set |true| +if a |"/"| was removed at the end of the file name; this means that the +user will have a chance to issue special instructions online just before +the fonts are loaded. + +@d check_fonts==@+if fonts_not_loaded then load_fonts + +@<Glob...@>= +@!interaction:boolean; {is the user allowed to type specials online?} +@!fonts_not_loaded:boolean; {have the \.{TFM} files still not been input?} +@!font_name:array[internal_font_number] of str_number; {current font names} +@!font_area:array[internal_font_number] of str_number; {current font areas} +@!font_at:array[internal_font_number] of scaled; {current font ``at'' sizes} + +@ @<Set init...@>= +interaction:=false; fonts_not_loaded:=true; +font_name[title_font]:=default_title_font; +font_name[label_font]:=default_label_font; +font_name[gray_font]:=default_gray_font; +font_name[slant_font]:=null_string; +font_name[logo_font]:=logo_font_name; +for k:=title_font to logo_font do + begin font_area[k]:=null_string; font_at[k]:=0; + end; + +@ After the following procedure has been performed, there will be no +turning back; the fonts will have been firmly established in +\.{GFtoDVI}'s memory. + +@<Declare the procedure called |load_fonts|@>= +procedure load_fonts; +label done,continue,found,not_found; +var @!f:internal_font_number; +@!i:four_quarters; {font information word} +@!j,@!k,@!v:integer; {registers for initializing font tables} +@!m:title_font..slant_font+area_code; {keyword found} +@!n1:0..longest_keyword; {buffered character being checked} +@!n2:pool_pointer; {pool character being checked} +begin if interaction then @<Get online special input@>; +fonts_not_loaded:=false; +for f:=title_font to logo_font do + if (f<>slant_font)or(length(font_name[f])>0) then + begin if length(font_area[f])=0 then font_area[f]:=home_font_area; + pack_file_name(font_name[f],font_area[f],tfm_ext); + open_tfm_file; read_font_info(f,font_at[f]); + if font_area[f]=home_font_area then font_area[f]:=null_string; + dvi_font_def(f); {put the font name in the \.{DVI} file} + end; +@<Initialize global variables that depend on the font data@>; +end; + +@ @<Get online special input@>= +loop@+ begin not_found: print_nl('Special font substitution: '); +@.Special font subst...@> + continue: input_ln; + if line_length=0 then goto done; + @<Search buffer for valid keyword; if successful, |goto found|@>; + print('Please say, e.g., "grayfont foo" or "slantfontarea baz".'); + goto not_found; + found: @<Update the font name or area@>; + print('OK; any more? '); goto continue; + end; +done: + +@ @<Search buffer for valid keyword; if successful, |goto found|@>= +buf_ptr:=0; buffer[line_length]:=" "; +while buffer[buf_ptr]<>" " do incr(buf_ptr); +for m:=title_font to slant_font+area_code do if length(m)=buf_ptr then + begin n1:=0; n2:=str_start[m]; + while (n1<buf_ptr)and(buffer[n1]=str_pool[n2]) do + begin incr(n1); incr(n2); + end; + if n1=buf_ptr then goto found; + end + +@ @<Update the font name or area@>= +incr(buf_ptr); str_room(line_length-buf_ptr); +while buf_ptr<line_length do + begin append_char(buffer[buf_ptr]); incr(buf_ptr); + end; +if m>area_code then font_area[m-area_code]:=make_string +else begin font_name[m]:=make_string; font_area[m]:=null_string; + font_at[m]:=0; + end; +init_str_ptr:=str_ptr + +@* Shipping pages out. +The following routines are used to write the \.{DVI} file. They have +been copied from \TeX, but simplified; we don't have to handle +nearly as much generality as \TeX\ does. + +Statistics about the entire set of pages that will be shipped out must be +reported in the \.{DVI} postamble. The global variables |total_pages|, +|max_v|, |max_h|, and |last_bop| are used to record this information. + +@<Glob...@>= +@!total_pages:integer; {the number of pages that have been shipped out} +@!max_v:scaled; {maximum height-plus-depth of pages shipped so far} +@!max_h:scaled; {maximum width of pages shipped so far} +@!last_bop:integer; {location of previous |bop| in the \.{DVI} output} + +@ @<Set init...@>= +total_pages:=0; max_v:=0; max_h:=0; last_bop:=-1; + +@ The \.{DVI} bytes are output to a buffer instead of being written directly +to the output file. This makes it possible to reduce the overhead of +subroutine calls. + +The output buffer is divided into two parts of equal size; the bytes found +in |dvi_buf[0..half_buf-1]| constitute the first half, and those in +|dvi_buf[half_buf..dvi_buf_size-1]| constitute the second. The global +variable |dvi_ptr| points to the position that will receive the next +output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal +to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that +is about to be invaded next is sent to the output and |dvi_limit| is +changed to its other value. Thus, there is always at least a half buffer's +worth of information present, except at the very beginning of the job. + +Bytes of the \.{DVI} file are numbered sequentially starting with 0; +the next byte to be generated will be number |dvi_offset+dvi_ptr|. + +@<Types ...@>= +@!dvi_index=0..dvi_buf_size; {an index into the output buffer} + +@ Some systems may find it more efficient to make |dvi_buf| a |packed| +array, since output of four bytes at once may be facilitated. +@^system dependencies@> + +@<Glob...@>= +@!dvi_buf:array[dvi_index] of eight_bits; {buffer for \.{DVI} output} +@!half_buf:dvi_index; {half of |dvi_buf_size|} +@!dvi_limit:dvi_index; {end of the current half buffer} +@!dvi_ptr:dvi_index; {the next available buffer address} +@!dvi_offset:integer; {|dvi_buf_size| times the number of times the + output buffer has been fully emptied} + +@ Initially the buffer is all in one piece; we will output half of it only +after it first fills up. + +@<Set init...@>= +half_buf:=dvi_buf_size div 2; dvi_limit:=dvi_buf_size; dvi_ptr:=0; +dvi_offset:=0; + +@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling +|write_dvi(a,b)|. It is safe to assume that |a| and |b+1| will both be +multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on +many machines to use efficient methods to pack four bytes per word and to +output an array of words with one system call. +@^system dependencies@> + +@p procedure write_dvi(@!a,@!b:dvi_index); +var k:dvi_index; +begin for k:=a to b do write(dvi_file,dvi_buf[k]); +end; + +@ To put a byte in the buffer without paying the cost of invoking a procedure +each time, we use the macro |dvi_out|. + +@d dvi_out(#)==@+begin dvi_buf[dvi_ptr]:=#; incr(dvi_ptr); + if dvi_ptr=dvi_limit then dvi_swap; + end + +@p procedure dvi_swap; {outputs half of the buffer} +begin if dvi_limit=dvi_buf_size then + begin write_dvi(0,half_buf-1); dvi_limit:=half_buf; + dvi_offset:=dvi_offset+dvi_buf_size; dvi_ptr:=0; + end +else begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit:=dvi_buf_size; + end; +end; + +@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr| +will be a multiple of~4. + +@<Empty the last bytes out of |dvi_buf|@>= +if dvi_limit=half_buf then write_dvi(half_buf,dvi_buf_size-1); +if dvi_ptr>0 then write_dvi(0,dvi_ptr-1) + +@ The |dvi_four| procedure outputs four bytes in two's complement notation, +without risking arithmetic overflow. + +@p procedure dvi_four(@!x:integer); +begin if x>=0 then dvi_out(x div @'100000000) +else begin x:=x+@'10000000000; + x:=x+@'10000000000; + dvi_out((x div @'100000000) + 128); + end; +x:=x mod @'100000000; dvi_out(x div @'200000); +x:=x mod @'200000; dvi_out(x div @'400); +dvi_out(x mod @'400); +end; + +@ Here's a procedure that outputs a font definition. + +@d select_font(#)==dvi_out(fnt_num_0+#) {set current font to \#} + +@p procedure dvi_font_def(@!f:internal_font_number); +var k:integer; {index into |str_pool|} +begin dvi_out(fnt_def1); +dvi_out(f);@/ +dvi_out(qo(font_check[f].b0)); +dvi_out(qo(font_check[f].b1)); +dvi_out(qo(font_check[f].b2)); +dvi_out(qo(font_check[f].b3));@/ +dvi_four(font_size[f]); +dvi_four(font_dsize[f]);@/ +dvi_out(length(font_area[f])); +dvi_out(length(font_name[f])); +@<Output the font name whose internal number is |f|@>; +end;@/ +@t\4@>@<Declare the procedure called |load_fonts|@>@; + +@ @<Output the font name whose internal number is |f|@>= +for k:=str_start[font_area[f]] to str_start[font_area[f]+1]-1 do + dvi_out(str_pool[k]); +for k:=str_start[font_name[f]] to str_start[font_name[f]+1]-1 do + dvi_out(str_pool[k]) + +@ The |typeset| subroutine typesets any eight-bit character. + +@p procedure typeset(@!c:eight_bits); +begin if c>=128 then dvi_out(set1); +dvi_out(c); +end; + +@ The |dvi_scaled| subroutine takes a |real| value |x| and outputs +a decimal approximation to |x/unity|, correct to one decimal place. + +@p procedure dvi_scaled(@!x:real); +var @!n:integer; {an integer approximation to |10*x/unity|} +@!m:integer; {the integer part of the answer} +@!k:integer; {the number of digits in |m|} +begin n:=round(x/6553.6); +if n<0 then + begin dvi_out("-"); n:=-n; + end; +m:=n div 10; k:=0; +repeat incr(k); buffer[k]:=(m mod 10)+"0"; m:=m div 10; +until m=0; +repeat dvi_out(buffer[k]); decr(k); +until k=0; +if n mod 10 <> 0 then + begin dvi_out("."); dvi_out((n mod 10)+"0"); + end; +end; + +@ At the end of the program, we must finish things off by writing the +post\-amble. An integer variable~|k| will be declared for use by this routine. + +@<Finish the \.{DVI} file and |goto final_end|@>= +begin dvi_out(post); {beginning of the postamble} +dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-5; {|post| location} +dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp} +dvi_four(1000); {magnification factor} +dvi_four(max_v); dvi_four(max_h);@/ +dvi_out(0); dvi_out(3); {`\\{max\_push}' is said to be 3}@/ +dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/ +if not fonts_not_loaded then + for k:=title_font to logo_font do + if length(font_name[k])>0 then dvi_font_def(k); +dvi_out(post_post); dvi_four(last_bop); dvi_out(dvi_id_byte);@/ +k:=4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's} +while k>0 do + begin dvi_out(223); decr(k); + end; +@<Empty the last bytes out of |dvi_buf|@>; +goto final_end; +end + +@* Rudimentary typesetting. +One of \.{GFtoDVI}'s little duties is to be a mini-\TeX: It must be able +to typeset the equivalent of `\.{\\hbox\{}$\langle$string$\rangle$\.\}' for +a given string of ASCII characters, using either the title font or the +label font. + +The |hbox| procedure does this. The width, height, and depth of the +box defined by string~|s| in font~|f| are computed in global variables +|box_width|, |box_height|, and |box_depth|. + +The task would be trivial if it weren't for ligatures and kerns, which +are implemented here in full generality. (Infinite looping is possible +if the \.{TFM} file is malformed; \.{TFtoPL} will diagnose such problems.) + +We assume that |" "| is a space character; character code @'40 will not +be typeset unless it is accessed via a ligature. + +If parameter |send_it| is |false|, we merely want to know the box dimensions. +Otherwise typesetting commands are also sent to +the \.{DVI} file; we assume in this case that font |f| has already been +selected in the \.{DVI} file as the current font. + +@d set_cur_r==if k<end_k then cur_r:=qi(str_pool[k]) + else cur_r:=bchar + +@p procedure hbox(@!s:str_number;@!f:internal_font_number;@!send_it:boolean); +label continue, done; +var @!k,@!end_k,@!max_k:pool_pointer; {indices into |str_pool|} +@!i,@!j:four_quarters; {font information words} +@!cur_l:0..256; {character to the left of the ``cursor''} +@!cur_r:min_quarterword..non_char; {character to the right of the ``cursor''} +@!bchar:min_quarterword..non_char; {right boundary character} +@!stack_ptr:0..lig_lookahead; {number of entries on |lig_stack|} +@!l:font_index; {pointer to lig/kern instruction} +@!kern_amount:scaled; {extra space to be typeset} +@!hd:eight_bits; {height and depth indices for a character} +@!x:scaled; {temporary register} +@!save_c:ASCII_code; {character temporarily blanked out} +begin box_width:=0; box_height:=0; box_depth:=0;@/ +k:=str_start[s]; max_k:=str_start[s+1]; +save_c:=str_pool[max_k]; str_pool[max_k]:=" "; +while k<max_k do + begin if str_pool[k]=" " then @<Typeset a space in font |f| and advance~|k|@> + else begin end_k:=k; + repeat incr(end_k); until str_pool[end_k]=" "; + kern_amount:=0; cur_l:=256; stack_ptr:=0; bchar:=font_bchar[f]; + set_cur_r; suppress_lig:=false; +continue: @<If there's a ligature or kern at the cursor position, + update the cursor data structures, possibly advancing~|k|; continue + until the cursor wants to move right@>; + @<Typeset character |cur_l|, if it exists in the font; + also append an optional kern@>; + @<Move the cursor to the right and |goto continue|, if there's + more work to do in the current word@>; + end; {now |k=end_k|} + end; +str_pool[max_k]:=save_c; +end; + +@ @<Glob...@>= +@!box_width:scaled; {width of box constructed by |hbox|} +@!box_height:scaled; {height of box constructed by |hbox|} +@!box_depth:scaled; {depth of box constructed by |hbox|} +@!lig_stack:array[1..lig_lookahead] of quarterword; {inserted ligature chars} +@!dummy_info:four_quarters; {fake |char_info| for nonexistent character} +@!suppress_lig:boolean; {should we bypass checking for ligatures next time?} + +@ @<Set init...@>= +dummy_info.b0:=qi(0); dummy_info.b1:=qi(0); dummy_info.b2:=qi(0); +dummy_info.b3:=qi(0); + +@ @<Typeset a space...@>= +begin box_width:=box_width+space(f); +if send_it then + begin dvi_out(right4); dvi_four(space(f)); + end; +incr(k); +end + +@ @<If there's a ligature...@>= +if(cur_l<font_bc[f])or(cur_l>font_ec[f]) then + begin i:=dummy_info; + if cur_l=256 then l:=bchar_label[f]@+else l:=non_address; + end +else begin i:=char_info(f)(cur_l); + if char_tag(i)<>lig_tag then l:=non_address + else begin l:=lig_kern_start(f)(i); j:=font_info[l].qqqq; + if skip_byte(j)>stop_flag then l:=lig_kern_restart(f)(j); + end; + end; +if suppress_lig then suppress_lig:=false +else while l<qi(kern_base[f]) do + begin j:=font_info[l].qqqq; + if next_char(j)=cur_r then if skip_byte(j)<=stop_flag then + if op_byte(j)>=kern_flag then + begin kern_amount:=char_kern(f)(j); goto done; + end + else @<Carry out a ligature operation, updating the cursor structure + and possibly advancing~|k|; |goto continue| if the cursor doesn't + advance, otherwise |goto done|@>; + if skip_byte(j)>=stop_flag then goto done; + l:=l+skip_byte(j)+1; + end; +done: + +@ At this point |i| contains |char_info| for |cur_l|. + +@<Typeset character...@>= +if char_exists(i) then + begin box_width:=box_width+char_width(f)(i)+kern_amount;@/ + hd:=height_depth(i); + x:=char_height(f)(hd); + if x>box_height then box_height:=x; + x:=char_depth(f)(hd); + if x>box_depth then box_depth:=x; + if send_it then + begin typeset(cur_l); + if kern_amount<>0 then + begin dvi_out(right4); dvi_four(kern_amount); + end; + end; + kern_amount:=0; + end + +@ @d pop_stack==begin decr(stack_ptr); + if stack_ptr>0 then cur_r:=lig_stack[stack_ptr] + else set_cur_r; + end + +@<Carry out a ligature operation, updating the cursor structure...@>= +begin case op_byte(j) of +1,5:cur_l:=qo(rem_byte(j)); +2,6:begin cur_r:=rem_byte(j); + if stack_ptr=0 then + begin stack_ptr:=1; + if k<end_k then incr(k) {a non-space character is consumed} + else bchar:=non_char; {the right boundary character is consumed} + end; + lig_stack[stack_ptr]:=cur_r; + end; +3,7,11:begin cur_r:=rem_byte(j); incr(stack_ptr); lig_stack[stack_ptr]:=cur_r; + if op_byte(j)=11 then suppress_lig:=true; + end; +othercases begin cur_l:=qo(rem_byte(j)); + if stack_ptr>0 then pop_stack + else if k=end_k then goto done + else begin incr(k); set_cur_r; + end; + end +endcases; +if op_byte(j)>3 then goto done; +goto continue; +end + +@ @<Move the cursor to the right and |goto continue|...@>= +cur_l:=qo(cur_r); +if stack_ptr>0 then + begin pop_stack; goto continue; + end; +if k<end_k then + begin incr(k); set_cur_r; goto continue; + end + +@* Gray fonts. +A proof diagram constructed by \.{GFtoDVI} +can be regarded as an array of rectangles, where each rectangle is either +blank or filled with a special symbol that we shall call $x$. A blank +rectangle represents a white pixel, while $x$ represents a black pixel. +Additional labels and reference lines are often superimposed on this +array of rectangles; hence it is usually best to choose a symbol $x$ that +has a somewhat gray appearance, although any symbol can actually be used. + +In order to construct such proofs, \.{GFtoDVI} needs to work with +a special type of font known as a ``gray font''; it's possible to +obtain a wide variety of different sorts of proofs by using different +sorts of gray fonts. The next few paragraphs explain exactly what gray +fonts are supposed to contain, in case you want to design your own. +@^gray fonts@> + +@ The simplest gray font contains only two characters, namely $x$ +and another symbol that is used for dots that identify key points. +If proofs with relatively large pixels are desired, a two-character +gray font is all that's needed. However, if the pixel size is to be +relatively small, practical considerations make a two-character +font too inefficient, since it requires the typesetting of tens +of thousands of tiny little characters; printing device drivers +rarely work very well when they are presented with data that is +so different from ordinary text. Therefore a gray font with small +pixels usually has a number of characters that replicate $x$ in +such a way that comparatively few characters actually need to be +typeset. + +Since many printing devices are not able to cope with +arbitrarily large or complex characters, it is not possible for a +single gray font to work well on all machines. In fact, +$x$ must have a width that is an integer multiple of the printing +device's unit of horizontal position, since rounding the positions of grey +characters would otherwise produce unsightly streaks on proof output. +Thus, there is no way to make the gray font as device-independent as +the rest of the system, in the sense that we would expect approximately +identical output on machines with different resolution. Fortunately, +proof sheets are rarely considered to be final documents; hence +\.{GFtoDVI} is set up to provide results that adapt suitably to +local conditions. + +@ With such constraints understood, we can now take a look at what +\.{GFtoDVI} expects to see in a gray font. The character~$x$ always +appears in position~1. It must have positive height~$h$ and positive +width~$w$; its depth and italic correction are ignored. + +Positions 2--120 of a gray font are reserved for special combinations +of $x$'s and blanks, stacked on top of each other. None of these character +codes need be present in the font; but if they are, the slots should be +occupied by characters of width~$w$ that have certain configurations of +$x$'s and blanks, prescribed for each character position. For example, +position~3 of the font should either contain no character at all, +or it should contain a character consisting of two $x$'s, one above +the other; one of these $x$'s should appear immediately above the +baseline, and the other should appear immediately below. + +It will be convenient to use a horizontal notation like `\.{XOXXO}' +to stand for a vertical stack of $x$'s and blanks. The convention +will be that the stack is built from bottom to top, and the topmost +rectangle should sit on the baseline. Thus, `\.{XOXXO}' stands +actually for a character of depth~$4h$ that looks like this: +$$\vcenter{\halign{\hfil#\hfil\cr +blank\cr +$x$\rlap{\qquad\raise8pt\hbox{\smash{\hbox{$\longleftarrow$ baseline}}}}\cr +$x$\cr +blank\cr +$x$\cr +}}$$ +(We use a horizontal notation instead of a vertical one in this explanation, +because column +vectors take too much space, and because the horizontal notation corresponds +to binary numbers in a convenient way.) + +Positions 1--63 of a gray font are reserved for the patterns \.X, \.{XO}, +\.{XX}, \.{XOO}, \.{XOX}, \dots, \.{XXXXXX}, just as in the normal +binary notation of the numbers 1--63. Positions 64--70 are reserved for +the special patterns \.{XOOOOOO}, \.{XXOOOOO}, \dots, \.{XXXXXXO}, +\.{XXXXXXX} of length seven; positions 71--78 are, similarly, reserved for +the length-eight patterns \.{XOOOOOOO} through \.{XXXXXXXX}. The +length-nine patterns \.{XOOOOOOOO} through \.{XXXXXXXXX} are assigned +to positions 79--87, the length-ten patterns to positions 88--97, +the length-eleven patterns to positions 98--108, and the length-twelve +patterns to positions 109--120. + +The following program sets a global array |c[1..120]| to the bit patterns +just described. Another array |d[1..120]| is set to contain only the next +higher bit; this determines the depth of the corresponding character. + +@<Set init...@>= +c[1]:=1; d[1]:=2; two_to_the[0]:=1; m:=1; +for k:=1 to 13 do two_to_the[k]:=2*two_to_the[k-1]; +for k:=2 to 6 do @<Add a full set of |k|-bit characters@>; +for k:=7 to 12 do @<Add special |k|-bit characters of the form \.{X..XO..O}@>; + +@ @<Glob...@>= +@!c:array[1..120] of 1..4095; {bit patterns for a gray font} +@!d:array[1..120] of 2..4096; {the superleading bits} +@!two_to_the:array[0..13] of 1..8192; {powers of 2} + +@ @<Add a full set of |k|-bit...@>= +begin n:=two_to_the[k-1]; +for j:=0 to n-1 do + begin incr(m); c[m]:=m; d[m]:=n+n; + end; +end + +@ @<Add special |k|-bit...@>= +begin n:=two_to_the[k-1]; +for j:=k downto 1 do + begin incr(m); d[m]:=n+n; + if j=k then c[m]:=n + else c[m]:=c[m-1]+two_to_the[j-1]; + end; +end + +@ Position 0 of a gray font is reserved for the ``dot'' character, which +should have positive height~$h'$ and positive width~$w'$. When \.{GFtoDVI} +wants to put a dot at some place $(x,y)$ on the figure, it positions +the dot character so that its reference point is at $(x,y)$. The +dot will be considered to occupy a rectangle $(x+\delta,y+\epsilon)$ +for $-w'\leq\delta\leq w'$ and $-h'\leq\epsilon\leq h'$; the rectangular +box for a label will butt up against the rectangle enclosing the dot. + +@ All other character positions of a gray font (namely, positions 121--255) +are unreserved, in the sense that they have no predefined meaning. +But \.{GFtoDVI} may access them via the ``character list'' feature of +\.{TFM} files, starting with any of the characters in positions +1--120. In such a case each succeeding character in a list should be +equivalent to two of its predecessors, horizontally adjacent to each other. +For example, in a character list like +$$53,\;121,\;122,\;123$$ +character 121 will stand for two 53's, character 122 for two 121's (i.e., +four 53's), and character 123 for two 122's (i.e., eight 53's). Since +position~53 contains the pattern \.{XXOXOX}, character~123 in this example +would have height~$h$, depth~$5h$, and width~$8w$, and it would stand for +the pattern +$$\vcenter{\halign{&$\hfil#\hfil$\cr +x&x&x&x&x&x&x&x\cr +&&&&&&&\cr +x&x&x&x&x&x&x&x\cr +&&&&&&&\cr +x&x&x&x&x&x&x&x\cr +x&x&x&x&x&x&x&x\cr}}$$ +Such a pattern is, of course, rather unlikely to occur in a \.{GF} file, +but \.{GFtoDVI} would be able to use if it were present. Designers +of gray fonts should provide characters only for patterns that they think +will occur often enough to make the doubling worthwhile. For example, +the character in position 120 (\.{XXXXXXXXXXXX}), or whatever is the +tallest stack of $x$'s present in the font, is a natural candidate for +repeated doubling. + +Here's how \.{GFtoDVI} decides what characters of the gray font will be used, +given a configuration of black and white pixels: If there are no black +pixels, stop. Otherwise look at the top row that contains at least one +black pixel, and the eleven rows that follow. For each such column, +find the largest~$k$ such that $1\leq k\leq120$ and the gray font contains +character~$k$ and the pattern assigned to position~$k$ appears in the +given column. Typeset character $k$ (unless no such character exists) +and erase the corresponding black pixels; use doubled characters, +if they are present in the gray font, if two or more consecutive equal +characters need to be typeset. Repeat the same process on the remaining +configuration, until all the black pixels have been erased. + +If all characters in positions 1--120 are present, this process is guaranteed to +take care of at least six rows each time; and it usually takes care of +twelve, since all patterns that contain at most one ``run'' of $x$'s +are present. + +@ Fonts have optional parameters, as described in Appendix~F of {\sl The +\TeX book}, and some of these are important in gray fonts. The +slant parameter~$s$, if nonzero, will cause \.{GFtoDVI} to skew its +output; in this case the character $x$ will presumably be a parallelogram +with a corresponding slant, rather than the usual rectangle. \MF's +coordinate $(x,y)$ will appear in physical position $(xw+yhs,yh)$ +on the proofsheets. + +Parameter number~8 of a gray font specifies the thickness of rules +that go on the proofs. If this parameter is zero, \TeX's default +rule thickness (0.4\thinspace pt) will be used. + +The other parameters of a gray font are ignored by \.{GFtoDVI}, but +it is conventional to set the font space parameter to~$w$ and the +xheight parameter to~$h$. + +@ For best results the designer of a gray font should choose $h$ and~$w$ +so that the user's \.{DVI}-to-hardcopy software will not make any +rounding errors. Furthermore, the dot should be an even number~$2m$ of +pixels in diameter, and the rule thickness should work out to an +even number~$2n$ of pixels; then the dots and rules will be centered on +the correct positions, in case of integer coordinates. Gray fonts +are almost always intended for particular output devices, even though +`\.{DVI}' stands for `device independent'; we use \.{DVI} files for \MF\ +proofs chiefly because software to print \.{DVI} files is already in place. + +@* Slant fonts. +\.{GFtoDVI} also makes use of another special type of font, if it is +necessary to typeset slanted rules. The format of such so-called +``slant fonts'' is quite a bit simpler than the format of gray fonts. + +A slant font should contain exactly $n$ characters, in positions 1 to~$n$, +where the character in position~$k$ represents a slanted line $k$ units +tall, starting at the baseline. These lines all have a fixed slant ratio~$s$. + +The following simple algorithm is used to typeset a rule that is $m$ units +high: Compute $q=\lceil m/n\rceil$; then typeset $q$~characters of +approximately equal size, namely $(m\bmod q)$ copies of character number +$\lceil m/q\rceil$ and $q-(m\bmod q)$ copies of character number +$\lfloor m/q\rfloor$. For example, if $n=15$ and $m=100$, we have $q=7$; +a 100-unit-high rule will be composed of 7~pieces, using characters +14,~14, 14, 14, 14, 15,~15. + +@<Glob...@>= +@!rule_slant:real; {the slant ratio $s$ in the slant font, + or zero if there is no slant font} +@!slant_n:integer; {the number of characters in the slant font} +@!slant_unit:real; {the number of scaled points in the slant font unit} +@!slant_reported:real; {invalid slant ratio reported to the user} + +@ \.{GFtoDVI} looks only at the height of character $n$, so the \.{TFM} file +need not be accurate about the heights of the other characters. (This is +fortunate, since \.{TFM} format allows at most 16 different heights per font.) + +The width of character~$k$ should be $k/n$ times $s$ times the height of +character~$n$. + +The slant parameter of a slant file should be $s$. It is customary to +set the |default_rule_thickness| parameter (number~8) to the thickness of +the slanted rules, but \.{GFtoDVI} doesn't look at it. + +@ For best results on a particular output device, it is usually wise to +choose the `unit' in the above discussion to be an integer number of pixels, +and to make it no larger than the default rule thickness in the gray font +being used. + +@ @<Initialize glob...@>= +if length(font_name[slant_font])=0 then rule_slant:=0.0 +else begin rule_slant:=slant(slant_font)/unity; + slant_n:=font_ec[slant_font]; + i:=char_info(slant_font)(slant_n); + slant_unit:=char_height(slant_font)(height_depth(i))/slant_n; + end; +slant_reported:=0.0; + +@ The following error message is given when an absent slant has been +requested. + +@p procedure slant_complaint(@!r:real); +begin if abs(r-slant_reported)>0.001 then + begin print_nl('Sorry, I can''t make diagonal rules of slant ',r:10:5,'!'); +@.Sorry, I can't...@> + slant_reported:=r; + end; +end; + +@* Representation of rectangles. +OK---the preliminary spadework has now been done. We're ready at last +to concentrate on \.{GFtoDVI}'s {\sl raison d'\^^Detre}. + +One of the most interesting tasks remaining is to make +a ``map'' of the labels that have been allocated. +There usually aren't a great many labels, so we don't need fancy data +structures; but we do make use of linked nodes containing nine fields. +The nodes generally represent rectangular boxes according to the +following conventions: +\smallskip\hang\noindent +|xl|, |xr|, |yt|, and |yb| are the left, right, top, and bottom locations +of a rectangle, expressed in \.{DVI} coordinates. (This program uses +scaled points as \.{DVI} coordinates. Since \.{DVI} coordinates increase +as one moves down the page, |yb| will be greater than |yt|.) +\smallskip\hang\noindent +|xx| and |yy| are the coordinates of the reference point of a box to be +typeset from this node, again in \.{DVI} coordinates. +\smallskip\hang\noindent +|prev| and |next| point to the predecessor and successor of this node. +Sometimes the nodes are singly linked and only |next| is relevant; otherwise +the nodes are doubly linked in order of their |yy| coordinates, so that we +can move down by going to |next|, or up by going to |prev|. +\smallskip\hang\noindent +|info| is the number of a string associated with this node. +\smallskip\noindent + +The nine fields of a node appear in nine global arrays. +Null pointers are denoted by |null|, which happens to be zero. + +@d null=0 + +@<Types ...@>= +@!node_pointer=null..max_labels; + +@ @<Glob...@>= +@!xl,@!xr,@!yt,@!yb:array[1..max_labels] of scaled; {boundary coordinates} +@!xx,@!yy:array[0..max_labels] of scaled; {reference coordinates} +@!prev,@!next:array[0..max_labels] of node_pointer; {links} +@!info:array[1..max_labels] of str_number; {associated strings} +@!max_node:node_pointer; {the largest node in use} +@!max_height:scaled; {greatest difference between |yy| and |yt|} +@!max_depth:scaled; {greatest difference between |yb| and |yy|} + + +@ It's easy to allocate a new node (unless no more room is left): + +@p function get_avail:node_pointer; +begin incr(max_node); +if max_node=max_labels then abort('Too many labels and/or rules!'); +@.Too many labels@> +get_avail:=max_node; +end; + +@ The doubly linked nodes are sorted by |yy| coordinates so that we don't have +to work too hard to find nearest neighbors or to determine if rectangles overlap. +The first node in the doubly linked rectangle list is always in location~0, +and the last node is always in location |max_labels|; the |yy| coordinates +of these nodes are very small and very large, respectively. + +@d end_of_list==max_labels + +@<Set init...@>= +yy[0]:=-@'10000000000; yy[end_of_list]:=@'10000000000; + +@ The |node_ins| procedure inserts a new rectangle, represented by node~|p|, +into the doubly linked list. There's a second parameter, |q|; node~|q| should +already be in the doubly linked list, preferably with |yy[q]| near |yy[p]|. + +@p procedure node_ins(@!p,@!q:node_pointer); +var @!r:node_pointer; {for tree traversal} +begin if yy[p]>=yy[q] then + begin repeat r:=q; q:=next[q];@+until yy[p]<=yy[q]; + next[r]:=p; prev[p]:=r; next[p]:=q; prev[q]:=p; + end +else begin repeat r:=q; q:=prev[q];@+until yy[p]>=yy[q]; + prev[r]:=p; next[p]:=r; prev[p]:=q; next[q]:=p; + end; +if yy[p]-yt[p]>max_height then max_height:=yy[p]-yt[p]; +if yb[p]-yy[p]>max_depth then max_depth:=yb[p]-yy[p]; +end; + +@ The data structures need to be initialized for each character in the +\.{GF} file. + +@<Initialize variables for the next character@>= +max_node:=0; next[0]:=end_of_list; prev[end_of_list]:=0; +max_height:=0; max_depth:=0; + +@ The |overlap| subroutine determines whether or not the rectangle specified +in node~|p| has a nonempty intersection with some rectangle in the doubly +linked list. Again |q|~is a parameter that gives us a starting point +in the list. We assume that |q<>end_of_list|, so that |next[q]| is meaningful. + +@p function overlap(@!p,@!q:node_pointer):boolean; +label exit; +var @!y_thresh:scaled; {cutoff value to speed the search} +@!x_left,@!x_right,@!y_top,@!y_bot:scaled; {boundaries to test for overlap} +@!r:node_pointer; {runs through the neighbors of |q|} +begin x_left:=xl[p]; x_right:=xr[p]; y_top:=yt[p]; y_bot:=yb[p]; +@<Look for overlaps in the successors of node |q|@>; +@<Look for overlaps in node |q| and its predecessors@>; +overlap:=false; +exit: end; + +@ @<Look for overlaps in the successors of node |q|@>= +y_thresh:=y_bot+max_height; r:=next[q]; +while yy[r]<y_thresh do + begin if y_bot > yt[r] then if x_left<xr[r] then + if x_right>xl[r] then if y_top<yb[r] then + begin overlap:=true; return; + end; + r:=next[r]; + end + +@ @<Look for overlaps in node |q| and its predecessors@>= +y_thresh:=y_top-max_depth; r:=q; +while yy[r]>y_thresh do + begin if y_bot > yt[r] then if x_left<xr[r] then + if x_right>xl[r] then if y_top<yb[r] then + begin overlap:=true; return; + end; + r:=prev[r]; + end + +@ Nodes that represent dots instead of labels satisfy the following +constraints: +$$\vcenter{\halign{#\hfil&\quad#\hfil\cr +|info[p]<0;|&|p>=first_dot|;\cr +|xl[p]=xx[p]-dot_width|,&|xr[p]=xx[p]+dot_width|;\cr +|yt[p]=yy[p]-dot_height|,&|yb[p]=yy[p]+dot_height|.\cr}}$$ + +The |nearest_dot| subroutine finds a node whose reference point is as +close as possible to a given position, ignoring nodes that are too close. +More precisely, the ``nearest'' node +minimizes$$d(q,p)=\max\bigl(\vert |xx|[q]-|xx|[p]\vert, + \vert |yy|[q]-|yy|[p]\vert\bigr)$$ over all nodes~|q| +with |d(q,p)>=d0|. We call the subroutine |nearest_dot| because it is used only +when the doubly linked list contains nothing but dots. + +The routine also sets the global variable |twin| to |true|, if there is a +node |q<>p| with |d(q,p)<d0|. + +@ @<Glob...@>= +@!first_dot:node_pointer; {the node address where dots begin} +@!twin:boolean; {is there a nearer dot than the ``nearest'' dot?} + +@ If there is no nearest dot, the value |null| is returned; +otherwise a pointer to the nearest dot is returned. + +@p function nearest_dot(@!p:node_pointer; @!d0:scaled):node_pointer; +var @!best_q:node_pointer; {value to return} +@!d_min,@!d:scaled; {distances} +begin twin:=false; best_q:=0; d_min:=@'2000000000; +@<Search for the nearest dot in nodes following |p|@>; +@<Search for the nearest dot in nodes preceding |p|@>; +nearest_dot:=best_q; +end; + +@ @<Search for the nearest dot in nodes following |p|@>= +q:=next[p]; +while yy[q]<yy[p]+d_min do + begin d:=abs(xx[q]-xx[p]); + if d<yy[q]-yy[p] then d:=yy[q]-yy[p]; + if d<d0 then twin:=true + else if d<d_min then + begin d_min:=d; best_q:=q; + end; + q:=next[q]; + end + +@ @<Search for the nearest dot in nodes preceding |p|@>= +q:=prev[p]; +while yy[q]>yy[p]-d_min do + begin d:=abs(xx[q]-xx[p]); + if d<yy[p]-yy[q] then d:=yy[p]-yy[q]; + if d<d0 then twin:=true + else if d<d_min then + begin d_min:=d; best_q:=q; + end; + q:=prev[q]; + end + +@* Doing the labels. +Each ``character'' in the \.{GF} file is preceded by a number of special +commands that define labels, titles, rules, etc. We store these away, +to be considered later when the |boc| command appears. The |boc| +command establishes the size information by which labels and rules +can be positioned, so we spew out the label information as soon as +we see the |boc|. The gray pixels will be typeset after all the labels +for a particular character have been finished. + +@ Here is the part of \.{GFtoDVI} that stores information preceding a~|boc|. +It comes into play when |cur_gf| is between |xxx1| and~|no_op|, inclusive. + +@d font_change(#)==if fonts_not_loaded then + begin #; end + else print_nl('(Tardy font change will be ignored (byte ', +@.Tardy font change...@> + cur_loc:1,')!)') + +@<Process a no-op command@>= +begin k:=interpret_xxx; +case k of +no_operation: do_nothing; +title_font,label_font,gray_font,slant_font:font_change(font_name[k]:=cur_string; + font_area[k]:=null_string;font_at[k]:=0;init_str_ptr:=str_ptr); +title_font+area_code,label_font+area_code,gray_font+area_code, + slant_font+area_code:@| + font_change(font_area[k-area_code]:=cur_string;init_str_ptr:=str_ptr); +title_font+at_code,label_font+at_code,gray_font+at_code, + slant_font+at_code:@| + font_change(font_at[k-at_code]:=get_yyy;init_str_ptr:=str_ptr); +rule_thickness_code:rule_thickness:=get_yyy; +rule_code:@<Store a rule@>; +offset_code:@<Override the offsets@>; +x_offset_code:x_offset:=get_yyy; +y_offset_code:y_offset:=get_yyy; +title_code:@<Store a title@>; +null_string:@<Store a label@>; +end; {there are no other cases} +end + +@ The following quantities are cleared just before reading the +\.{GF} commands pertaining to a character. + +@<Glob...@>= +@!rule_thickness:scaled; {the current rule thickness + (zero means use the default)} +@!offset_x,@!offset_y:scaled; {the current offsets for images} +@!x_offset,@!y_offset:scaled; {the current offsets for labels} +@!pre_min_x,@!pre_max_x,@!pre_min_y,@!pre_max_y:scaled; + {extreme values of coordinates preceding a character, in \MF\ pixels} + +@ @<Initialize variables for the next character@>= +rule_thickness:=0; +offset_x:=0; offset_y:=0; x_offset:=0; y_offset:=0; +pre_min_x:=@'2000000000; pre_max_x:=-@'2000000000; +pre_min_y:=@'2000000000; pre_max_y:=-@'2000000000; + +@ @<Override the offsets@>= +begin offset_x:=get_yyy; offset_y:=get_yyy; +end + +@ Rules that will need to be drawn are kept in a linked list accessible +via |rule_ptr|, in last-in-first-out order. The nodes of this list will +never get into the doubly linked list, and indeed these nodes use different +field conventions entirely (because rules may be slanted). + +@d x0==xl {starting |x| coordinate of a stored rule} +@d y0==yt {starting |y| coordinate (in scaled \MF\ pixels)} +@d x1==xr {ending |x| coordinate of a stored rule} +@d y1==yb {ending |y| coordinate of a stored rule} +@d rule_size==xx {thickness of a stored rule, in scaled points} + +@<Glob...@>= +@!rule_ptr:node_pointer; {top of the stack of remembered rules} + +@ @<Store a rule@>= +begin p:=get_avail; next[p]:=rule_ptr; rule_ptr:=p;@/ +x0[p]:=get_yyy; y0[p]:=get_yyy; x1[p]:=get_yyy; y1[p]:=get_yyy; +if x0[p]<pre_min_x then pre_min_x:=x0[p]; +if x0[p]>pre_max_x then pre_max_x:=x0[p]; +if y0[p]<pre_min_y then pre_min_y:=y0[p]; +if y0[p]>pre_max_y then pre_max_y:=y0[p]; +if x1[p]<pre_min_x then pre_min_x:=x1[p]; +if x1[p]>pre_max_x then pre_max_x:=x1[p]; +if y1[p]<pre_min_y then pre_min_y:=y1[p]; +if y1[p]>pre_max_y then pre_max_y:=y1[p]; +rule_size[p]:=rule_thickness; +end + +@ Titles and labels are, likewise, stored temporarily in singly linked lists. +In this case the lists are first-in-first-out. +Variables |title_tail| and |label_tail| point to the most recently inserted +title or label; variables |title_head| and |label_head| +point to the beginning of the list. (A standard coding trick is used +for |label_head|, which is kept in |next[end_of_list]|; we have +|label_tail=end_of_list| when the list is empty.) + +The |prev| field in nodes of the temporary label list specifies the +type of label, so we call it |lab_typ|. + +@d lab_typ==prev {the type of a stored label (|"/"..."8"|)} +@d label_head==next[end_of_list] + +@<Glob...@>= +@!label_tail:node_pointer; {tail of the queue of remembered labels} +@!title_head,@!title_tail:node_pointer; {head and tail of the queue for titles} + +@ We must start the lists out empty. + +@<Initialize variables for the next char...@>= +rule_ptr:=null; +title_head:=null; title_tail:=null; label_head:=null; label_tail:=end_of_list; +first_dot:=max_labels; + +@ @<Store a title@>= +begin p:=get_avail; info[p]:=cur_string; +if title_head=null then title_head:=p +else next[title_tail]:=p; +title_tail:=p; +end + +@ We store the coordinates of each label in units of \MF\ pixels; they +will be converted to \.{DVI} coordinates later. + +@<Store a label@>= +if (label_type<"/")or(label_type>"8") then + print_nl('Bad label type precedes byte ',cur_loc:1,'!') +@.Bad label type...@> +else begin p:=get_avail; next[label_tail]:=p; label_tail:=p;@/ + lab_typ[p]:=label_type; info[p]:=cur_string;@/ + xx[p]:=get_yyy; yy[p]:=get_yyy; + if xx[p]<pre_min_x then pre_min_x:=xx[p]; + if xx[p]>pre_max_x then pre_max_x:=xx[p]; + if yy[p]<pre_min_y then pre_min_y:=yy[p]; + if yy[p]>pre_max_y then pre_max_y:=yy[p]; + end + +@ The process of ferreting everything away comes to an abrupt halt +when a |boc| command is sensed. The following steps are performed +at such times: + +@<Process a character@>= +begin check_fonts; +@<Finish reading the parameters of the |boc|@>; +@<Get ready to convert \MF\ coordinates to \.{DVI} coordinates@>; +@<Output the |bop| and the title line@>; +print('[',total_pages:1); update_terminal; {print a progress report} +@<Output all rules for the current character@>; +@<Output all labels for the current character@>; +do_pixels; +dvi_out(eop); {finish the page} +@<Adjust the maximum page width@>; +print(']'); update_terminal; +end + +@ @<Finish reading the parameters of the |boc|@>= +if cur_gf=boc then + begin ext:=signed_quad; {read the character code} + char_code:=ext mod 256; + if char_code<0 then char_code:=char_code+256; + ext:=(ext-char_code) div 256; + k:=signed_quad; {read and ignore the prev pointer} + min_x:=signed_quad; {read the minimum $x$ coordinate} + max_x:=signed_quad; {read the maximum $x$ coordinate} + min_y:=signed_quad; {read the minimum $y$ coordinate} + max_y:=signed_quad; {read the maximum $y$ coordinate} + end +else begin ext:=0; char_code:=get_byte; {|cur_gf=boc1|} + min_x:=get_byte; max_x:=get_byte; min_x:=max_x-min_x;@/ + min_y:=get_byte; max_y:=get_byte; min_y:=max_y-min_y; + end; +if max_x-min_x>widest_row then abort('Character too wide!') +@.Character too wide@> + +@ @<Glob...@>= +@!char_code,@!ext:integer; {the current character code and extension} +@!min_x,@!max_x,@!min_y,@!max_y:integer; {character boundaries, in pixels} +@!x,@!y:integer; {current painting position, in pixels} +@!z:integer; {initial painting position in row, relative to |min_x|} + +@ \MF\ coordinates $(x,y)$ are converted to \.{DVI} coordinates by the +following routine. Real values |x_ratio|, |y_ratio|, and |slant_ratio| +will have been calculated based on the gray font; |scaled| values +|delta_x| and |delta_y| will have been computed so that, in the absence +of slanting and offsets, the \MF\ coordinates |(min_x,max_y+1)| will correspond +to the \.{DVI} coordinates $(0,50\,\rm pt)$. + +@p procedure convert(@!x,@!y:scaled); +begin x:=x+x_offset; y:=y+y_offset; +dvi_y:=-round(y_ratio*y)+delta_y; +dvi_x:=round(x_ratio*x+slant_ratio*y)+delta_x; +end; + +@ @<Glob...@>= +@!x_ratio,@!y_ratio,@!slant_ratio:real; {conversion factors} +@!unsc_x_ratio,@!unsc_y_ratio,@!unsc_slant_ratio:real; + {ditto, times |unity|} +@!fudge_factor:real; {unconversion factor} +@!delta_x,@!delta_y:scaled; {magic constants used by |convert|} +@!dvi_x,@!dvi_y:scaled; {outputs of |convert|, in scaled points} +@!over_col:scaled; {overflow labels start here} +@!page_height,page_width:scaled; {size of the current page} + +@ @<Initialize global variables that depend on the font data@>= +i:=char_info(gray_font)(1); +if not char_exists(i) then abort('Missing pixel char!'); +@.Missing pixel char@> +unsc_x_ratio:=char_width(gray_font)(i); +x_ratio:=unsc_x_ratio/unity; +unsc_y_ratio:=char_height(gray_font)(height_depth(i)); +y_ratio:=unsc_y_ratio/unity; +unsc_slant_ratio:=slant(gray_font)*y_ratio; +slant_ratio:=unsc_slant_ratio/unity; +if x_ratio*y_ratio=0 then abort('Vanishing pixel size!'); +@.Vanishing pixel size@> +fudge_factor:=(slant_ratio/x_ratio)/y_ratio; + +@ @<Get ready to convert...@>= +if pre_min_x<min_x*unity then offset_x:=offset_x+min_x*unity-pre_min_x; +if pre_max_y>max_y*unity then offset_y:=offset_y+max_y*unity-pre_max_y; +if pre_max_x>max_x*unity then pre_max_x:=pre_max_x div unity +else pre_max_x:=max_x; +if pre_min_y<min_y*unity then pre_min_y:=pre_min_y div unity +else pre_min_y:=min_y; +delta_y:=round(unsc_y_ratio*(max_y+1)-y_ratio*offset_y)+3276800; +delta_x:=round(x_ratio*offset_x-unsc_x_ratio*min_x); +if slant_ratio>=0 then + over_col:=round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*max_y) +else over_col:=round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*min_y); +over_col:=over_col+delta_x+10000000; +page_height:=round(unsc_y_ratio*(max_y+1-pre_min_y))+3276800-offset_y; +if page_height>max_v then max_v:=page_height; +page_width:=over_col-10000000 + +@ The |dvi_goto| subroutine outputs bytes to the \.{DVI} file that +will initiate typesetting at given \.{DVI} coordinates, assuming that +the current position of the \.{DVI} reader is $(0,0)$. This subroutine +begins by outputting a |push| command; therefore, a |pop| command should +be given later. That |pop| will restore the \.{DVI} position to $(0,0)$. + +@p procedure dvi_goto(@!x,@!y:scaled); +begin dvi_out(push); +if x<>0 then + begin dvi_out(right4); dvi_four(x); + end; +if y<>0 then + begin dvi_out(down4); dvi_four(y); + end; +end; + +@ @<Output the |bop| and the title line@>= +dvi_out(bop); incr(total_pages); dvi_four(total_pages); +dvi_four(char_code); dvi_four(ext); +for k:=3 to 9 do dvi_four(0); +dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-45;@/ +dvi_goto(0,655360); {the top baseline is 10\thinspace pt down} +if use_logo then + begin select_font(logo_font); hbox(small_logo,logo_font,true); + end; +select_font(title_font); hbox(time_stamp,title_font,true);@/ +hbox(page_header,title_font,true); dvi_scaled(total_pages*65536.0);@/ +if (char_code<>0)or(ext<>0) then + begin hbox(char_header,title_font,true); dvi_scaled(char_code*65536.0); + if ext<>0 then + begin hbox(ext_header,title_font,true); dvi_scaled(ext*65536.0); + end; + end; +if title_head<>null then + begin next[title_tail]:=null; + repeat hbox(left_quotes,title_font,true); + hbox(info[title_head],title_font,true); + hbox(right_quotes,title_font,true); + title_head:=next[title_head]; + until title_head=null; + end; +dvi_out(pop) + +@ @d tol==6554 {one tenth of a point, in \.{DVI} coordinates} + +@<Output all rules for the current character@>= +if rule_slant<>0 then select_font(slant_font); +while rule_ptr<>null do + begin p:=rule_ptr; rule_ptr:=next[p];@/ + if rule_size[p]=0 then rule_size[p]:=gray_rule_thickness; + if rule_size[p]>0 then + begin convert(x0[p],y0[p]); temp_x:=dvi_x; temp_y:=dvi_y; + convert(x1[p],y1[p]); + if abs(temp_x-dvi_x)<tol then @<Output a vertical rule@> + else if abs(temp_y-dvi_y)<tol then @<Output a horizontal rule@> + else @<Try to output a diagonal rule@>; + end; + end + +@ @<Glob...@>= +@!gray_rule_thickness:scaled; {thickness of rules, according to the gray font} +@!temp_x,@!temp_y:scaled; {temporary registers for intermediate calculations} + +@ @<Initialize glob...@>= +gray_rule_thickness:=default_rule_thickness(gray_font); +if gray_rule_thickness=0 then gray_rule_thickness:=26214; {0.4\thinspace pt} + +@ @<Output a vertical rule@>= +begin if temp_y>dvi_y then + begin k:=temp_y; temp_y:=dvi_y; dvi_y:=k; + end; +dvi_goto(dvi_x-(rule_size[p] div 2), dvi_y); +dvi_out(put_rule); dvi_four(dvi_y-temp_y); dvi_four(rule_size[p]); +dvi_out(pop); +end + +@ @<Output a horizontal rule@>= +begin if temp_x<dvi_x then + begin k:=temp_x; temp_x:=dvi_x; dvi_x:=k; + end; +dvi_goto(dvi_x,dvi_y+(rule_size[p] div 2)); +dvi_out(put_rule); dvi_four(rule_size[p]); dvi_four(temp_x-dvi_x); +dvi_out(pop); +end + +@ @<Try to output a diagonal rule@>= +if (rule_slant=0)or@| + (abs(temp_x+rule_slant*(temp_y-dvi_y)-dvi_x)>rule_size[p]) then + slant_complaint((dvi_x-temp_x)/(temp_y-dvi_y)) +else begin if temp_y>dvi_y then + begin k:=temp_y; temp_y:=dvi_y; dvi_y:=k;@/ + k:=temp_x; temp_x:=dvi_x; dvi_x:=k; + end; + m:=round((dvi_y-temp_y)/slant_unit); + if m>0 then + begin dvi_goto(dvi_x,dvi_y); + q:=((m-1) div slant_n)+1; k:=m div q; + p:=m mod q; q:=q-p; + @<Vertically typeset |q| copies of character |k|@>; + @<Vertically typeset |p| copies of character |k+1|@>; + dvi_out(pop); + end; + end + +@ @<Vertically typeset |q| copies of character |k|@>= +typeset(k); dy:=round(k*slant_unit); dvi_out(z4); dvi_four(-dy); +while q>1 do + begin typeset(k); dvi_out(z0); decr(q); + end + +@ @<Vertically typeset |p| copies of character |k+1|@>= +if p>0 then + begin incr(k); typeset(k); + dy:=round(k*slant_unit); dvi_out(z4); dvi_four(-dy); + while p>1 do + begin typeset(k); dvi_out(z0); decr(p); + end; + end + +@ Now we come to a more interesting part of the computation, where we +go through the stored labels and try to fit them in the illustration for +the current character, together with their associated dots. + +It would simplify font-switching slightly if we were to typeset the labels +first, but we find it desirable to typeset the dots first and then turn to the +labels. This procedure makes it possible for us to allow the dots to +overlap each other without allowing the labels to overlap. After the +dots are in place, we typeset all prescribed labels, that is, labels with a +|lab_typ| of |"1".."8"|; these, too, are allowed to overlap the dots and +each other. + +@<Output all labels for the current character@>= +overflow_line:=1; +if label_head<>null then + begin next[label_tail]:=null; select_font(gray_font); + @<Output all dots@>; + @<Find nearest dots, to help in label positioning@>; + select_font(label_font); + @<Output all prescribed labels@>; + @<Output all attachable labels@>; + @<Output all overflow labels@>; + end + +@ @<Glob...@>= +@!overflow_line:integer; {the number of labels that didn't fit, plus~1} + +@ A label that appears above its dot is considered to occupy a +rectangle of height~$h+\Delta$, depth~$d$, and width~$w+2\Delta$, where +$(h,w,d)$ are the height, width, and depth of the label computed by |hbox|, +and $\Delta$ is an additional amount of blank space that keeps labels from +coming too close to each other. (\.{GFtoDVI} arbitrarily defines $\Delta$ +to be one half the width of a space in the label font.) This label is +centered over its dot, with its baseline $d+h'$ above the center of the dot; +here $h'=|dot_height|$ is the height of character~0 in the gray font. + +Similarly, a label that appears below its dot is considered to occupy +a rectangle of height~$h$, depth~$d+\Delta$, and width~$w+2\Delta$; the +baseline is $h+h'$ below the center of the dot. + +A label at the right of its dot is considered to occupy a rectangle of +height~$h+\Delta$, depth~$d+\Delta$, and width~$w+\Delta$. Its +reference point can be found by starting at the center of the dot and +moving right $w'=|dot_width|$ (i.e., the width of character~0 in the +gray font), then moving down by half the x-height of the label font. +A label at the left of its dot is similar. + +A dot is considered to occupy a rectangle of height $2h'$ and width~$2w'$, +centered on the dot. + +When the label type is |"1"| or more, the labels +are put into the doubly linked list unconditionally. + Otherwise they are put into the list +only if we can find a way to fit them in without +overlapping any previously inserted rectangles. + +@<Glob...@>= +@!delta:scaled; {extra padding to keep labels from being too close} +@!half_x_height:scaled; {amount to drop baseline of label below the dot center} +@!thrice_x_height:scaled; {baseline separation for overflow labels} +@!dot_width,@!dot_height:scaled; {$w'$ and $h'$ in the discussion above} + +@ @<Initialize global variables that depend on the font data@>= +i:=char_info(gray_font)(0); +if not char_exists(i) then abort('Missing dot char!'); +@.Missing dot char@> +dot_width:=char_width(gray_font)(i); +dot_height:=char_height(gray_font)(height_depth(i)); +delta:=space(label_font) div 2; +thrice_x_height:=3*x_height(label_font); +half_x_height:=thrice_x_height div 6; + +@ Here is a subroutine that computes the rectangle boundaries +|xl[p]|, |xr[p]|, |yt[p]|, |yb[p]|, and the reference point coordinates +|xx[p]|,~|yy[p]|, for a label that is to be placed above a dot. +The coordinates of the dot's center are assumed given in |dvi_x| +and |dvi_y|; the |hbox| subroutine is assumed to have +already computed the height, width, and depth of the label box. + +@p procedure top_coords(@!p:node_pointer); +begin xx[p]:=dvi_x-(box_width div 2); xl[p]:=xx[p]-delta; +xr[p]:=xx[p]+box_width+delta;@/ +yb[p]:=dvi_y-dot_height; yy[p]:=yb[p]-box_depth; +yt[p]:=yy[p]-box_height-delta; +end; + +@ The other three label positions are handled by similar routines. + +@p procedure bot_coords(@!p:node_pointer); +begin xx[p]:=dvi_x-(box_width div 2); xl[p]:=xx[p]-delta; +xr[p]:=xx[p]+box_width+delta;@/ +yt[p]:=dvi_y+dot_height; yy[p]:=yt[p]+box_height; +yb[p]:=yy[p]+box_depth+delta; +end; +@# +procedure right_coords(@!p:node_pointer); +begin xl[p]:=dvi_x+dot_width; xx[p]:=xl[p]; xr[p]:=xx[p]+box_width+delta;@/ +yy[p]:=dvi_y+half_x_height; yb[p]:=yy[p]+box_depth+delta; +yt[p]:=yy[p]-box_height-delta; +end; +@# +procedure left_coords(@!p:node_pointer); +begin xr[p]:=dvi_x-dot_width; xx[p]:=xr[p]-box_width; xl[p]:=xx[p]-delta;@/ +yy[p]:=dvi_y+half_x_height; yb[p]:=yy[p]+box_depth+delta; +yt[p]:=yy[p]-box_height-delta; +end; + +@ @<Output all dots@>= +p:=label_head; first_dot:=max_node+1; +while p<>null do + begin convert(xx[p],yy[p]); xx[p]:=dvi_x; yy[p]:=dvi_y; + if lab_typ[p]<"5" then + @<Enter a dot for label |p| in the rectangle list, + and typeset the dot@>; + p:=next[p]; + end + +@ We plant links between dots and their labels by using (or abusing) the +|xl| and |info| fields, which aren't needed for their normal purposes. + +@d dot_for_label==xl +@d label_for_dot==info + +@<Enter a dot...@>= +begin q:=get_avail; dot_for_label[p]:=q; label_for_dot[q]:=p;@/ +xx[q]:=dvi_x; xl[q]:=dvi_x-dot_width; xr[q]:=dvi_x+dot_width;@/ +yy[q]:=dvi_y; yt[q]:=dvi_y-dot_height; yb[q]:=dvi_y+dot_height;@/ +node_ins(q,0);@/ +dvi_goto(xx[q],yy[q]); dvi_out(0); dvi_out(pop); +end + +@ Prescribed labels are now taken out of the singly linked list and +inserted into the doubly linked list. + +@<Output all prescribed labels@>= +q:=end_of_list; {|label_head=next[q]|} +while next[q]<>null do + begin p:=next[q]; + if lab_typ[p]>"0" then + begin next[q]:=next[p]; + @<Enter a prescribed label for node |p| into the rectangle list, + and typeset it@>; + end + else q:=next[q]; + end + +@ @<Enter a prescr...@>= +begin hbox(info[p],label_font,false); {Compute the size of this label} +dvi_x:=xx[p]; dvi_y:=yy[p]; +if lab_typ[p]<"5" then r:=dot_for_label[p]@+else r:=0; +case lab_typ[p] of +"1","5":top_coords(p); +"2","6":left_coords(p); +"3","7":right_coords(p); +"4","8":bot_coords(p); +end; {no other cases are possible} +node_ins(p,r);@/ +dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop); +end + +@ \.{GFtoDVI}'s algorithm for positioning the ``floating'' labels +was devised by Arthur~L. Samuel. +@^Samuel, Arthur Lee@> +It tries to place labels in a priority order, based on the position of +the nearest dot to a given dot. If that dot, for example, lies in the first +octant (i.e., east to northeast of the given dot), the given label will +be put into the west slot unless that slot is already blocked; then the +south slot will be tried, etc. + +First we need to compute the octants. We also note if two or more dots +are nearly coincident, since Samuel's algorithm modifies the priority +order on that case. The information is temporarily recorded in the |xr| array. + +@d octant==xr {octant code for nearest dot, plus 8 for coincident dots} + +@<Find nearest dots, to help in label positioning@>= +p:=label_head; +while p<>null do + begin if lab_typ[p]<="0" then + @<Compute the octant code for floating label |p|@>; + p:=next[p]; + end; + +@ There's a sneaky way to identify octant numbers, represented by the +code shown here. (Remember that |y|~coordinates increase downward +in the \.{DVI} convention.) + +@d first_octant=0 +@d second_octant=1 +@d third_octant=2 +@d fourth_octant=3 +@d fifth_octant=7 +@d sixth_octant=6 +@d seventh_octant=5 +@d eighth_octant=4 + +@<Compute the octant code for floating label |p|@>= +begin r:=dot_for_label[p]; q:=nearest_dot(r,10); +if twin then octant[p]:=8@+else octant[p]:=0; +if q<>null then + begin dx:=xx[q]-xx[r]; dy:=yy[q]-yy[r]; + if dy>0 then octant[p]:=octant[p]+4; + if dx<0 then incr(octant[p]); + if dy>dx then incr(octant[p]); + if -dy>dx then incr(octant[p]); + end; +end + +@ A procedure called |place_label| will try to place the remaining +labels in turn. If it fails, we ``disconnect'' the dot from this +label so that an unlabeled dot will not appear as a reference in the +overflow column. + +@<Output all attachable labels@>= +q:=end_of_list; {now |next[q]=label_head|} +while next[q]<>null do + begin p:=next[q]; r:=next[p]; s:=dot_for_label[p]; + if place_label(p) then next[q]:=r + else begin label_for_dot[s]:=null; {disconnect the dot} + if lab_typ[p]="/" then next[q]:=r {remove label from list} + else q:=p; {retain label in list for the overflow column} + end; + end + +@ Here is the |place_label| routine, which uses the previously computed +|octant| information as a heuristic. If the label can be placed, it +is inserted into the rectangle list and typeset. + +@p function place_label(@!p:node_pointer):boolean; +label exit, found; +var @!oct:0..15; {octant code} +@!dfl:node_pointer; {saved value of |dot_for_label[p]|} +begin hbox(info[p],label_font,false); {Compute the size of this label} +dvi_x:=xx[p]; dvi_y:=yy[p]; +@<Find non-overlapping coordinates, if possible, and |goto| found; + otherwise set |place_label:=false| and |return|@>; +found:node_ins(p,dfl);@/ +dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop); +place_label:=true; +exit:end; + +@ @<Find non-overlapping coordinates, if possible...@>= +dfl:=dot_for_label[p]; oct:=octant[p]; +@<Try the first choice for label direction@>; +@<Try the second choice for label direction@>; +@<Try the third choice for label direction@>; +@<Try the fourth choice for label direction@>; +xx[p]:=dvi_x; yy[p]:=dvi_y; dot_for_label[p]:=dfl; {no luck; restore the coordinates} +place_label:=false; return + +@ @<Try the first choice for label direction@>= +case oct of +first_octant,eighth_octant,second_octant+8,seventh_octant+8: left_coords(p); +second_octant,third_octant,first_octant+8,fourth_octant+8: bot_coords(p); +fourth_octant,fifth_octant,third_octant+8,sixth_octant+8: right_coords(p); +sixth_octant,seventh_octant,fifth_octant+8,eighth_octant+8: top_coords(p); +end; +if not overlap(p,dfl) then goto found + +@ @<Try the second choice for label direction@>= +case oct of +first_octant,fourth_octant,fifth_octant+8,eighth_octant+8: bot_coords(p); +second_octant,seventh_octant,third_octant+8,sixth_octant+8: left_coords(p); +third_octant,sixth_octant,second_octant+8,seventh_octant+8: right_coords(p); +fifth_octant,eighth_octant,first_octant+8,fourth_octant+8: top_coords(p); +end; +if not overlap(p,dfl) then goto found + +@ @<Try the third choice for label direction@>= +case oct of +first_octant,fourth_octant,sixth_octant+8,seventh_octant+8: top_coords(p); +second_octant,seventh_octant,fourth_octant+8,fifth_octant+8: right_coords(p); +third_octant,sixth_octant,first_octant+8,eighth_octant+8: left_coords(p); +fifth_octant,eighth_octant,second_octant+8,third_octant+8: bot_coords(p); +end; +if not overlap(p,dfl) then goto found + +@ @<Try the fourth choice for label direction@>= +case oct of +first_octant,eighth_octant,first_octant+8,eighth_octant+8: right_coords(p); +second_octant,third_octant,second_octant+8,third_octant+8: top_coords(p); +fourth_octant,fifth_octant,fourth_octant+8,fifth_octant+8: left_coords(p); +sixth_octant,seventh_octant,sixth_octant+8,seventh_octant+8: bot_coords(p); +end; +if not overlap(p,dfl) then goto found + +@ @<Output all overflow labels@>= +@<Remove all rectangles from list, except for dots that have labels@>; +p:=label_head; +while p<>null do + begin @<Typeset an overflow label for |p|@>; + p:=next[p]; + end + +@ When we remove a dot that couldn't be labeled, we set its |next| field +to the preceding node that survives, so that we can use the |nearest_dot| +routine later. (This is a bit of a kludge.) + +@<Remove all rectangles from list, except for dots that have labels@>= +p:=next[0]; +while p<>end_of_list do + begin q:=next[p]; + if (p<first_dot) or (label_for_dot[p]=null) then + begin r:=prev[p]; next[r]:=q; prev[q]:=r; next[p]:=r; + end; + p:=q; + end + +@ Now we have to insert |p| into the list temporarily, because of the +way |nearest_dot| works. + +@<Typeset an overflow label for |p|@>= +begin r:=next[dot_for_label[p]]; s:=next[r]; t:=next[p]; +next[p]:=s; prev[s]:=p; next[r]:=p; prev[p]:=r;@/ +q:=nearest_dot(p,0);@/ +next[r]:=s; prev[s]:=r; next[p]:=t; {remove |p| again} +incr(overflow_line); +dvi_goto(over_col,overflow_line*thrice_x_height+655360); +hbox(info[p],label_font,true); +if q<>null then + begin hbox(equals_sign,label_font,true); + hbox(info[label_for_dot[q]],label_font,true); + hbox(plus_sign,label_font,true); + dvi_scaled((xx[p]-xx[q])/x_ratio+(yy[p]-yy[q])*fudge_factor); + dvi_out(","); + dvi_scaled((yy[q]-yy[p])/y_ratio); + dvi_out(")"); + end; +dvi_out(pop); +end + +@ @<Adjust the maximum page width@>= +if overflow_line>1 then page_width:=over_col+10000000; + {overflow labels are estimated to occupy $10^7\,$sp} +if page_width>max_h then max_h:=page_width + +@* Doing the pixels. +The most interesting part of \.{GFtoDVI} is the way it makes use of a gray +font to typeset the pixels of a character. In fact, the author must admit having +great fun devising the algorithms below. Perhaps the reader will also +enjoy reading them. + +The basic idea will be to use an array of 12-bit integers to represent the next +twelve rows that need to be typeset. The binary expansions of these integers, +reading from least significant bit to most significant bit, will represent +pixels from top to bottom. + +@ We have already used such a binary representation in the tables +|c[1..120]| and |d[1..120]| of bit patterns and lengths that are potentially +present in a gray font; we shall now use those tables to compute +an auxiliary array |b[0..4095]|. Given a 12-bit number~$v$, the gray-font +character appropriate to $v$'s binary pattern will be~|b[v]|. If no +character should be typeset for this pattern in the current row, +|b[v]| will be~0. + +The array |b| can have many different configurations, depending on how +many characters are actually present in the gray font. But +it's not difficult to compute |b| by going through the existing characters +in increasing order and marking all patterns~$x$ to which they apply. + +@<Initialize glob...@>= +for k:=0 to 4095 do b[k]:=0; +for k:=font_bc[gray_font] to font_ec[gray_font] do + if k>=1 then if k<=120 then + if char_exists(char_info(gray_font)(k)) then + begin v:=c[k]; + repeat b[v]:=k; v:=v+d[k]; + until v>4095; + end; + +@ We also compute an auxiliary array |rho[0..4095]| such that $\\{rho}[v]=2^j$ +when |v| is an odd multiple of~$2^j$; we also set $\\{rho}[0]=2^{12}$. + +@<Initialize g...@>= +for j:=0 to 11 do + begin k:=two_to_the[j]; v:=k; + repeat rho[v]:=k; v:=v+k+k; + until v>4095; + end; +rho[0]:=4096; + +@ @<Glob...@>= +@!b:array[0..4095] of 0..120; {largest existing character for a given pattern} +@!rho:array[0..4095] of 1..4096; {the ``ruler function''} + +@ But how will we use these tables? Let's imagine that the \.{DVI} file +already contains instructions that have selected the gray font and moved +to the proper horizontal coordinate for the row that we wish to process next. +Let's suppose that 12-bit patterns have been set up in array~|a|, and that +the global variables |starting_col| and |finishing_col| are known such +that |a[j]| is zero unless |starting_col<=j<=finishing_col|. Here's what +we can do, assuming that appropriate local variables and labels have +been declared: + +@<Typeset the pixels of the current row@>= +j:=starting_col; +loop@+ begin while (j<=finishing_col)and(b[a[j]]=0) do incr(j); + if j>finishing_col then goto done; + dvi_out(push); @<Move to column |j| in the \.{DVI} output@>; + repeat v:=b[a[j]]; a[j]:=a[j]-c[v]; + k:=j; incr(j); + while b[a[j]]=v do + begin a[j]:=a[j]-c[v]; incr(j); + end; + k:=j-k; @<Output the equivalent of |k| copies of character |v|@>; + until b[a[j]]=0; + dvi_out(pop); + end; +done: + +@ @<Move to column |j| in the \.{DVI} output@>= +dvi_out(right4); +dvi_four(round(unsc_x_ratio*j+unsc_slant_ratio*y)+delta_x) + +@ The doubling-up property of gray font character lists is utilized here. + +@<Output the equivalent of |k| copies of character |v|@>= +reswitch: if k=1 then typeset(v) +else begin i:=char_info(gray_font)(v); + if char_tag(i)=list_tag then {|v| has a successor} + begin if odd(k) then typeset(v); + k:=k div 2; v:=qo(rem_byte(i)); goto reswitch; + end + else repeat typeset(v); decr(k); + until k=0; + end + +@ @<Glob...@>= +@!a:array[0..widest_row] of 0..4095; {bit patterns for twelve rows} + +@ In order to use the approach above, we need to be able to initialize +array~|a|, and we need to be able to keep it up to date as new rows +scroll by. A moment's thought about the problem reveals that we will either +have to read an entire character from the \.{GF} file into memory, +or we'll need to adopt a coroutine-like approach: A single \\{skip} +command in the \.{GF} file might need to be processed in pieces, since +it might generate more rows of zeroes than we are ready to absorb +all at once into~|a|. + +The coroutine method actually turns out to be quite simple, so we shall +introduce a global variable |blank_rows|, which tells how many rows of +blanks should be generated before we read the \.{GF} instructions +for another row. + +@<Glob...@>= +@!blank_rows:integer; + {rows of blanks carried over from a previous \.{GF} command} + +@ Initialization and updating of~|a| can now be handled as follows, +if we introduce another variable~|l| that is set initially to~1: + +@<Add more rows to |a|, until 12-bit entries are obtained@>= +repeat @<Put the bits for the next row, times |l|, into |a|@>; +l:=l+l; decr(y); +until l=4096; + +@ As before, |cur_gf| will contain the first \.{GF} command that has +not yet been interpreted. + +@<Put the bits...@>= +if blank_rows>0 then decr(blank_rows) +else if cur_gf<>eoc then + begin x:=z; + if starting_col>x then starting_col:=x; + @<Read and process \.{GF} commands until coming to the end of this row@>; + end; + +@ @d do_skip==z:=0; paint_black:=false +@d end_with(#)==begin #; cur_gf:=get_byte; goto done1;@+end +@d five_cases(#)==#,#+1,#+2,#+3,#+4 +@d eight_cases(#)==#,#+1,#+2,#+3,#+4,#+5,#+6,#+7 +@d thirty_two_cases(#)==eight_cases(#),eight_cases(#+8), + eight_cases(#+16), eight_cases(#+24) +@d sixty_four_cases(#)==thirty_two_cases(#), thirty_two_cases(#+32) + +@<Read and process...@>= +loop @+begin continue: case cur_gf of + sixty_four_cases(0): k:=cur_gf; + paint1:k:=get_byte; + paint2:k:=get_two_bytes; + paint3:k:=get_three_bytes; + eoc:goto done1; + skip0:end_with(blank_rows:=0; do_skip); + skip1:end_with(blank_rows:=get_byte; do_skip); + skip2:end_with(blank_rows:=get_two_bytes; do_skip); + skip3:end_with(blank_rows:=get_three_bytes; do_skip); + sixty_four_cases(new_row_0),sixty_four_cases(new_row_0+64), + thirty_two_cases(new_row_0+128),five_cases(new_row_0+160): + end_with(z:=cur_gf-new_row_0;paint_black:=true); + xxx1,xxx2,xxx3,xxx4,yyy,no_op:begin skip_nop; goto continue; + end; + othercases bad_gf('Improper opcode') + endcases;@/ + @<Paint |k| bits and read another command@>; + end; +done1: + +@ @<Paint |k| bits and read another command@>= +if x+k>finishing_col then finishing_col:=x+k; +if paint_black then for j:=x to x+k-1 do a[j]:=a[j]+l; +paint_black:=not paint_black; +x:=x+k; +cur_gf:=get_byte + +@ When the current row has been typeset, all entries of |a| will be even; +we want to divide them by~2 and incorporate a new row with $l=2^{11}$. +However, if they are all multiples of~4, we actually want to divide by~4 +and incorporate two new rows, with $l=2^{10}$ and $l=2^{11}$. In general, +we want to divide by the maximum possible power of~2 and add the corresponding +number of new rows; that's where the |rho|~array comes in handy: + +@<Advance to the next row that needs to be typeset; + or |return|, if we're all done@>= +l:=rho[a[starting_col]]; +for j:=starting_col+1 to finishing_col do if l>rho[a[j]] then l:=rho[a[j]]; +if l=4096 then + if cur_gf=eoc then return + else begin y:=y-blank_rows; blank_rows:=0; l:=1; + starting_col:=z; finishing_col:=z; + end +else begin while a[starting_col]=0 do incr(starting_col); + while a[finishing_col]=0 do decr(finishing_col); + for j:=starting_col to finishing_col do a[j]:=a[j] div l; + l:=4096 div l; + end + +@ We now have constructed the major components of the necessary routine; +it simply remains to glue them all together in the proper framework. + +@p procedure do_pixels; +label done,done1,reswitch,continue,exit; +var @!paint_black:boolean; {the paint switch} +@!starting_col,@!finishing_col:0..widest_row; {currently nonzero area} +@!j:0..widest_row; {for traversing that area} +@!l:integer; {power of two used to manipulate bit patterns} +@!i:four_quarters; {character information word} +@!v:eight_bits; {character corresponding to a pixel pattern} +begin select_font(gray_font); +delta_x:=delta_x+round(unsc_x_ratio*min_x); +for j:=0 to max_x-min_x do a[j]:=0; +l:=1; z:=0; starting_col:=0; finishing_col:=0; y:=max_y+12; paint_black:=false; +blank_rows:=0; cur_gf:=get_byte; +loop@+ begin @<Add more rows...@>; + dvi_goto(0,delta_y-round(unsc_y_ratio*y)); @<Typeset the pixels...@>; + dvi_out(pop); @<Advance to the next...@>; + end; +exit:end; + +@* The main program. +Now we are ready to put it all together. This is where \.{GFtoDVI} starts, +and where it ends. + +@p begin initialize; {get all variables initialized} +@<Initialize the strings@>; +start_gf; {open the input and output files} +@<Process the preamble@>; +cur_gf:=get_byte; init_str_ptr:=str_ptr; +loop@+ begin @<Initialize variables for the next character@>; + while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>; + if cur_gf=post then @<Finish the \.{DVI} file and |goto final_end|@>; + if cur_gf<>boc then if cur_gf<>boc1 then abort('Missing boc!'); +@.Missing boc@> + @<Process a character@>; + cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr]; + end; +final_end:end. + +@ The main program needs a few global variables in order to do its work. + +@<Glob...@>= +@!k,@!m,@!p,@!q,@!r,@!s,@!t,@!dx,@!dy:integer; {general purpose registers} +@!time_stamp:str_number; {the date and time when the input file was made} +@!use_logo:boolean; {should \MF's logo be put on the title line?} + +@ \MF\ sets the opening string to 32 bytes that give date and time as follows: +$$\hbox{|' METAFONT output yyyy.mm.dd:tttt'|}$$ +We copy this to the \.{DVI} file, but remove the `\.{METAFONT}' part so that +it can be replaced by its proper logo. + +@<Process the preamble@>= +if get_byte<>pre then bad_gf('No preamble'); +@.No preamble@> +if get_byte<>gf_id_byte then bad_gf('Wrong ID'); +@.Wrong ID@> +k:=get_byte; {|k| is the length of the initial string to be copied} +for m:=1 to k do append_char(get_byte); +dvi_out(pre); dvi_out(dvi_id_byte); {output the preamble} +dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp} +dvi_four(1000); {magnification factor} +dvi_out(k); use_logo:=false; s:=str_start[str_ptr]; +for m:=1 to k do dvi_out(str_pool[s+m-1]); +if str_pool[s]=" " then + if str_pool[s+1]="M" then + if str_pool[s+2]="E" then + if str_pool[s+3]="T" then + if str_pool[s+4]="A" then + if str_pool[s+5]="F" then + if str_pool[s+6]="O" then + if str_pool[s+7]="N" then + if str_pool[s+8]="T" then + begin incr(str_ptr); str_start[str_ptr]:=s+9; use_logo:=true; + end; {we will substitute `\MF' for \.{METAFONT}} +time_stamp:=make_string + +@* System-dependent changes. +This section should be replaced, if necessary, by changes to the program +that are necessary to make \.{GFtoDVI} 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. +@^system dependencies@> + +@* Index. +Here is a list of the section numbers where each identifier is used. +Cross references to error messages and a few other tidbits of information +also appear. |