summaryrefslogtreecommitdiff
path: root/macros/text1/compugraphics_8600/fonttex.web
diff options
context:
space:
mode:
Diffstat (limited to 'macros/text1/compugraphics_8600/fonttex.web')
-rw-r--r--macros/text1/compugraphics_8600/fonttex.web1936
1 files changed, 1936 insertions, 0 deletions
diff --git a/macros/text1/compugraphics_8600/fonttex.web b/macros/text1/compugraphics_8600/fonttex.web
new file mode 100644
index 0000000000..6f2ce2858e
--- /dev/null
+++ b/macros/text1/compugraphics_8600/fonttex.web
@@ -0,0 +1,1936 @@
+%test version
+\font\twelvept=cmbx12
+\font\tentex=cmr10
+\def\topofcontents{\null
+ \def\titlepage{T}
+ \centerline{{\twelvept The FONTTEX Program}}
+ \vskip15pt
+ \centerline{Version 2, July 1986}
+ \hbox{\vbox{\hsize\the\hsize This work is
+ protected as an unpublished work under U.S. copyright laws.
+ Copyright $\copyright$ 1986 by WSUCSC. All rights Reserved.}}
+ \vskip18pt
+ \hbox{\vbox{\hsize\the\hsize This software is furnished under a
+ license for
+ use only on a single computer system and may be copied only
+ with the inclusion of the above copyright notice.
+ This software, or any other copies
+ thereof, may not be provided or otherwise made available to any
+ other person except for use on such system and to one who agrees to
+ these license terms. Title to and ownership of the software shall
+ at all times remain in WSUCSC.}}
+ \vfill}
+
+@* Introduction.
+ This version of fonttex writes font and driver information
+ into elements. Infile, a data file, must be assigned prior
+ the font data.
+
+ Fonttex reads a font description source element, and creates
+ a \TeX\ font file (TFM) and an 8600 driver element.
+
+ The font description source is divided into three sections.
+ The first section includes comments and parameters for the
+ entire font, the second describes the individual characters,
+ and the third provides information about ligatures, kerns,
+ linked math characters, and limit conventions on math
+ operators. Numeric information is given as integers,
+ or fractions depending on the context in which it
+ occurs. All sizes given will be scaled by the point size by
+ \TeX\ and so should reflect the sizes for 1-point
+ type.
+
+ The first section can include comments, which begin with a
+ "*" in column one of the line. It also includes definitions
+ of numeric parameters for the \TeX\ and driver font files.
+ The parameter definitions consist of an alphabetic name
+ beginning in column 1 and ending with one or more blanks;
+ and a numeric value. Comments and parameters can be
+ interspersed. This section is ended by a line containing a
+ dollar sign in column 1. Although the order the parameters
+ are given in is not significant, we will discuss them in a
+ conventional order. The first two parameters are the
+ scaling factors for the heights, widths, and depths of the
+ characters given in section 2. These scaling factors are
+ called |HUNITS| and |VUNITS|. Both have real values. All
+ character widths are implicitly multiplied by |HUNITS|, and
+ all heights and depths by |VUNITS|. For the Compugraphic 8600
+ we use |HUNITS|=1/54 and |VUNITS|=1/64, because of the internal
+ units used on the machine. The next several parameters give
+ information about the font needed by the 8600 driver.
+ |DEFFONT| specifies a font number on the typesetter. Unless
+ otherwise defined, all characters in this file come from
+ this font. |DEFWIDTH| gives the width multiplier. All
+ character widths are also multiplied by |DEFWIDTH|. Typically
+ |DEFWIDTH| is 1, but it could be less for condensed type and
+ more for expanded type. The remaining parameters are for
+ the \TeX\ font file. They all take real values.
+
+ There should be 128 character entries in section two.
+ Information about character is given in free format columns,
+ which are separated by one or more blanks. Beginning in
+ column 1 is the character mnemonic. Most fonts use standard
+ ascii codes, so that character 65 is A, etc. For this
+ reason, any single character name will be checked to see
+ that it corresponds to the appropriate ascii value. If not a
+ warning is issued.
+ Following this may be the keyword
+ LIGATURE or BUILD describing ligatures and built-up math
+ delimiters. This is omitted for ordinary characters. The
+ ligature keyword indicates that this character should be
+ substituted for a combination of two other characters.
+ Following the keyword should be the names of the two
+ characters separated by a plus sign. For example, fi
+ LIGATURE f+i and: ff LIGATURE f+f ffl LIGATURE ff+l.
+ For the
+ BUILD keyword, the character will be made from pieces.
+ There can be a top, bottom, middle, and extension character.
+ All are optional except the extension character. This is
+ specified as BUILD keyword=charname,keyword=charname,...
+ For instance, bigparen BUILD
+ TOP=parentop,MID=parenmid,BOT=parenmid.
+ The third field of
+ information is the character width, the fourth the height,
+ and the fifth the depth. Widths are given relative to the
+ point size and are implicitly multiplied by the |HUNITS|
+ parameter. Likewise height and depth are relative and are
+ multiplied by |VUNITS|. For example, the typical value of
+ |HUNITS| is 1/54. Then a character width given as 27 means
+ that the character is 0.5 times the point size in width. The
+ point size will be determined when the file is referred to
+ in \TeX , and the character sizes will be multiplied by it as
+ the file is read into \TeX . The sixth column gives 8600
+ commands and character codes. The commands have integer or
+ real parameters. Most characters will have a single character
+ code, and will be selected from the default font. An
+ alternate font can be specified by the F command. 8600 code
+ can also include point size commands, psuedo-italic
+ commands, etc. Refer to the module discussing ``Reading the
+ 8600 Codes.''
+
+ The final section specifies kerns, linked math characters,
+ and math operator conventions. A kern is an amount of
+ horizontal space to be subtracted between two characters.
+ It is specified as KERN A+W 5/100 for instance, to shave
+ 5/100 of the point size between occurences of A and W.
+ Linking occurs in math extension fonts where various sizes
+ of parentheses, brackets, and so forth occur. To link one
+ character to the next larger size of the same symbol,
+ specify LINK charname1 charname2 Finally, a character is
+ specified to be a mathop by the MATHOP keyword. (Common
+ mathops are sum and integral signs.) Following the keyword
+ is the character name and a real number. If the real number
+ is zero, limits for the operator will be centered above and
+ below it in display style. If non-zero, limits are set to
+ the right of the symbol, and the lower limit is moved left
+ by this amount.
+
+ Some further detail on the methods and data structures used
+ by Fonttex. Widths, heights, depths, and italic corrections
+ are stored in arrays of reals (type realarray). Entry -1
+ gives the highest numbered entry used so far. When a height,
+ depth, etc is read from the input file, procedure |AddToList|
+ is called to add the value to the array and return the index
+ of the entry in the array. If two or more characters have
+ the same dimension, the value is stored only once and the
+ characters have identical indices pointing to it. In the TFM
+ format font files, each character has an index into the
+ width, height, depth, italic correction arrays, and the
+ arrays of values are given separately. (See TUGboat volume
+ 2, no 1; or the comments in Sysdep at |ReadFontInfo| for more
+ details on the font file format.)
+
+ Ligatures are specified by giving the name of the two
+ "component" characters of the ligature. These are saved in
+ an array of records, "lig" until the entire pre-file has
+ been read. Then a pass is made through these records to look
+ up all the names of the component characters, and a another
+ pass is made through the lig records to group them by the
+ first character of the ligature and add a pointer in the
+ outstuff array for that character pointing to the
+ ligature/kern program. The same is done with kern
+ specifications and the final pass over both is done
+ simultaneously.
+
+ Extendible characters made from top, bottom, middle, and
+ extension pieces are marked at the character entry, and the
+ names of the pieces are saved in array ext until the file
+ has been read. The tag value and pointer into the ext array
+ are set in outstuff when the line is read in |ReadBuildStuff|,
+ but the character numbers for the ext array itself are not
+ found until after the entire file has been read.
+
+ When records are written to the driver and font files they
+ can be displayed by compiling Fonttex with "drvdump" and
+ "dump" respectively set to true.
+
+@ Following are a few macros and definitions used throughout program:
+
+@d incr(#) == # := # + 1
+@d decr(#) == # := # - 1
+@d do_nothing == begin; end
+@d ccat==@=||@>
+@f static == var
+@f value == var
+
+@ The beginning of the program.
+
+@p program fonttex(terminal,sysprint,fontpkt,drvpkt,infile);
+const
+ @<Global Constants@>@/
+type
+ @<Global Types@>@/
+var
+ @<Global Variables@>@/
+static@/
+ @^system dependencies@>
+ com_table :packed array[0..18] of string(7);@/
+value@/
+ com_table[0] := 'HDR'; {Header Record}@/
+ com_table[1] := 'CHWIDTH';{Character Width}@/
+ com_table[2] := 'RT'; {Reverse Type}@/
+ com_table[3] := 'PS'; {Point Size}@/
+ com_table[4] := 'SS'; {Set Size}@/
+ com_table[5] := 'VMF'; {Vertical Move Forward}@/
+ com_table[6] := 'VMR'; {Vertical Move Reverse}@/
+ com_table[7] := 'RW'; {Rule Width}@/
+ com_table[8] := 'RD'; {Rule Depth}@/
+ com_table[9] := 'IR'; {Insert Rule}@/
+ com_table[10] := 'SL'; {Slant Mode}@/
+ com_table[11] := 'RTWT'; {Reverse Type Window Top}@/
+ com_table[12] := 'RTWB'; {Reverse Type Window Bottom}@/
+ com_table[13] := 'AU'; {Auxiliary Character Set}@/
+ com_table[14] := 'F'; {Change Fonts}@/
+ com_table[15] := 'HMR'; {Horizontal Move Right}@/
+ com_table[16] := 'HML'; {Horizontal Move Left}@/
+ com_table[17] := 'TTS'; {8600 character, in decimal}@/
+ com_table[18] := 'MAXCMD';@/
+%include pasclib(asciicvt)@/
+
+@* Beginning section.
+This section includes some basic functions for reading the DVI file
+as well as a couple of procedures, like error and allcaps. Nothing
+tricky or noteworthy in these.
+
+@<Global Constants@>=
+ maxkerns = 255;
+ num_entries = 128; {maximum number of characters entered}
+
+@
+@<Global Types@>=
+ oneoftwo = packed 1..2;
+ oneoffour = packed 1..4;
+ bytes4 = packed record
+ byte0 :packed 0..255;
+ byte1 :packed 0..255;
+ case oneoftwo of
+ 1 :(rhword :packed 0..65535);
+ 2 :(byte2 :packed 0..255;
+ byte3 :packed 0..255)
+ end;
+ charindex = 0..127;@\
+ comm = (HDR,CHWIDTH,RT,PS,SS,VMF,VMR,RW,RD,IR,SL,
+ RTWT,RTWB,AU,F,HMR,HML,TTS,MAXCMD);@\
+ driverrec = record
+ cmd :integer;
+ case oneoftwo of
+ 1 :(param :shortreal);
+ 2 :(code :integer);
+ end;
+ extrec = record
+ sourceline:integer;
+ topname,botname,extname,midname :string(40);
+ top,bot,mid,ext :charindex;
+ end;
+ fonttypes = (standard,mathex,mathsy);
+ halves2 = packed record
+ lhword :packed 0..65535;
+ case oneoftwo of
+ 1 :(rhword :packed 0..65535);
+ 2 :(byte2 :packed 0..255;
+ byte3 :packed 0..255)
+ end;
+ kernrec = record
+ frstchar :charindex;
+ scndchar :charindex;
+ kernslot :integer;
+ end;
+ ligrec = record
+ sourceline :integer;
+ firstname :string(40);
+ scndname :string(40);
+ frstchar :charindex;
+ scndchar :charindex;
+ ligcode :charindex;
+ end;
+ memoryword = packed record case oneoffour of
+ 1 :(pts :shortreal);
+ 2 :(int :integer);
+ 3 :(twohalves :halves2);
+ 4 :(fourbytes :bytes4)
+ end;
+ outarray = array[charindex] of memoryword;
+ parameter = (slant,spacee,spstretch,spshrink,
+ xheight,quad,extraspace,
+ raisen1,raisen2,raisen3,
+ denom1,denom2,sup1,sup2,
+ sup3,sub1,sub2,supdrop,subdrop,
+ dlims1,dlims2,axisheight,
+ defthickness,bgopsp1,
+ bgopsp2,bgopsp3,
+ bgopsp4,bgopsp5);
+ realarray = array[-1..num_entries] of real;
+
+@ This function will convert whatever is passed to it into
+all capital letters. It translates lower case letters into upper case
+letters. All other characters outside of the range
+a$<$=character$<$=z are returned as their original value.
+
+@^system dependencies@>
+@p function allcaps(instring :string(40)) :string(40);
+var
+ i :integer;
+ character :char;
+ buildit :string(40);
+static
+ chtable :packed array['81'xc..'e9'xc] of char;
+value
+ chtable['81'xc] := 'A';
+ chtable['82'xc] := 'B';
+ chtable['83'xc] := 'C';
+ chtable['84'xc] := 'D';
+ chtable['85'xc] := 'E';
+ chtable['86'xc] := 'F';
+ chtable['87'xc] := 'G';
+ chtable['88'xc] := 'H';
+ chtable['89'xc] := 'I';
+ chtable['91'xc] := 'J';
+ chtable['92'xc] := 'K';
+ chtable['93'xc] := 'L';
+ chtable['94'xc] := 'M';
+ chtable['95'xc] := 'N';
+ chtable['96'xc] := 'O';
+ chtable['97'xc] := 'P';
+ chtable['98'xc] := 'Q';
+ chtable['99'xc] := 'R';
+ chtable['a2'xc] := 'S';
+ chtable['a3'xc] := 'T';
+ chtable['a4'xc] := 'U';
+ chtable['a5'xc] := 'V';
+ chtable['a6'xc] := 'W';
+ chtable['a7'xc] := 'X';
+ chtable['a8'xc] := 'Y';
+ chtable['a9'xc] := 'Z';
+begin
+ buildit := '';
+ for i := 1 to length(instring) do begin
+ readstr(substr(instring,i,1),character);
+ if character in ['a'..'z']
+ then buildit := buildit || str(chtable[character])
+ else buildit := buildit || str(character);
+ end;
+ allcaps := buildit;
+ end;
+
+@ This function receives an integer value and converts it to its
+hex value and returns that value as a string.
+
+@^system dependencies@>
+@p function hex(int :integer) :string(8);
+var
+ i :integer;
+ j :integer;
+ k16 :integer;
+ stri :string(8);
+static
+ hexarray :array[0..15] of string(1);
+value
+ hexarray[0] := '0';
+ hexarray[1] := '1';
+ hexarray[2] := '2';
+ hexarray[3] := '3';
+ hexarray[4] := '4';
+ hexarray[5] := '5';
+ hexarray[6] := '6';
+ hexarray[7] := '7';
+ hexarray[8] := '8';
+ hexarray[9] := '9';
+ hexarray[10] := 'A';
+ hexarray[11] := 'B';
+ hexarray[12] := 'C';
+ hexarray[13] := 'D';
+ hexarray[14] := 'E';
+ hexarray[15] := 'F';
+begin
+ stri := '';
+ i := int;
+ if i < 0 then begin
+ stri := 'FF';
+ i := 'FFFFFF'x + i + 1;
+ end;
+ k16 := 1048576;
+ repeat
+ if i >= k16
+ then begin
+ j := i div k16;
+ stri := stri || hexarray[j];
+ i := i - (j * k16);
+ end
+ else if length(stri) > 0
+ then stri := stri || '0';
+ k16 := k16 div 16;
+ until k16 = 1;
+ stri := stri || hexarray[i];
+ i := length(stri);
+ if (i = 1) or (i = 3) or (i = 5)
+ then stri := '0' || stri;
+ hex := stri;
+ end;
+
+@
+@<Global Types@>=
+ oneofsix = 1..6;
+
+@
+@<Global Variables@>=
+ dump :boolean;
+ fontpkt :file of memoryword;
+ inx :integer;
+ name :array[0..127] of string(40);
+ outcount :integer;
+ outrec :memoryword;
+
+@
+@p procedure writeout(outrec :memoryword;
+ outtype :oneofsix);
+var
+ length :integer;
+begin
+ if dump then begin
+ write(outcount:6,': ');
+ case outtype of
+ 1 :writeln('Pts=',outrec.pts);
+ 2 :begin
+ writeln('Fix=',outrec.int);
+ end;
+ 3 :with outrec.twohalves do
+ writeln('lhword=',lhword,',rhword=',rhword);
+ 4 :with outrec.fourbytes do
+ writeln(' Byte0=',byte0:3,'=>"',hex(byte0),'"',
+ ',Byte1=',byte1:3,'=>"',hex(byte1),'"',
+ ',Byte2=',byte2:3,'=>"',hex(byte2),'"',
+ ',Byte3=',byte3:3,'=>"',hex(byte3),'"');
+ 5 :with outrec.fourbytes do
+ writeln(' Width inx =',byte0:3,
+ ',Height inx= ',byte1 div 16:2,
+ ',Depth inx =',byte1 mod 16:2,
+ ',ItCor inx= ',byte2 div 4:2,
+ ',Tag =',byte2 mod 4:1,
+ ',Rem =',byte3:2,
+ ',chr="',name[inx],'"');
+ 6 :writeln(outrec.int:1);
+ end;{case}
+ end;{if}
+ outcount := outcount + 1; {count it}
+ fontpkt@@ := outrec;
+ put(fontpkt);
+ end; {writeout}
+
+@
+@<Global Types@>=
+@!error_severity = (fatal,notreallyfatal,overlookable);
+
+@
+@<Global Variables@>=
+@!currchar :charindex;
+@!errlin :integer;
+@!errlstr :string(10);
+@!errmax :integer;
+@!errmin :integer;
+@!errnum :integer;
+@!errstr :string(133);
+@!infile :text;
+@!lenfile :integer;
+@!lineno :integer;
+@!sysprint :text;
+
+@ The error procedure. When a byte is read that does not jive with
+what the program was expecting, it goes to this procedure with a
+return code of some kind and an appropriate message is printed to user.
+
+@p procedure error(err :integer;
+ howbad :error_severity);
+begin
+ if errlin = 0 then errlin := lineno;
+ {The caller can override the current line by setting errlin}
+ case err of
+ 0 :writeln('Empty input file');
+ 1 :writeln('Unrecognized keyword :',errstr);
+ 2 :begin
+ write('Number of entries must be specified');
+ writeln(' before the entries appear');
+ end;
+ 4 :begin
+ write('Integer expected but not found: next char=');
+ if eoln(infile)
+ then writeln('end of line')
+ else writeln(infile@@);
+ end;
+ 5 :writeln('Too many unique widths, depths, or heights');
+ 6 :begin
+ writeln('Entries in font file are out of sequence:');
+ writeln('The entry labelled ',errstr[1],
+ ' has a sequence ', 'of ',currchar:3,
+ ', not its Ascii value ',ordx[errstr[1]]:3);
+ end;
+ 7 :writeln('Ran out of entries in font file prematurely');
+ 8 :writeln('Real value given for parameter ',errstr,
+ ', but integer required');
+ 9 :writeln('The character names in a ligature or kern ',
+ 'have to be separated by a "+"');
+ 10 :writeln('Knave! No space should follow thine plus sign');
+ 11 :writeln('We expected to see a keyword here, not ',
+ errstr,'. Perhaps you have too many ',
+ 'character entries, or (gasp!) a typo');
+ 12 :writeln('You never specified "',errstr,
+ '" as a character name');
+ 13 :writeln('Keyword should be TOP,BOT,MID,EXT, not "',
+ errstr:3,'"');
+ 14 :writeln('You''ve specified parameters for both ',
+ 'mathex and mathsy fonts');
+ 15 :writeln('Expected to write ',lenfile:3,' records, ',
+ 'but actually wrote ',outcount:3);
+ 16 :writeln('After character name should be BUILD, ',
+ 'LIGATURE, or integer, not ',errstr);
+ 17 :writeln('Invalid value for parameter ',errlstr,', ',
+ errnum,' is <',errmin,' or >',errmax);
+ 18 :writeln('TTS codes must be within 0 to 127');
+ 19 :writeln(errlstr,' is not a valid 8600 command');
+ 20 :writeln('There is no 8600 code given for character ',
+ errstr:1);
+ 21 :writeln('Bad character for 8600 code :',infile@@);
+ 24 :writeln('Major font number should be between 1 and ',
+ maxfont:3);
+ 27 :writeln('Fontname too long, truncated to 8 characters');
+ 28 :writeln('Invalid input values for tracing, ',
+ 'record "',errlstr,'" skipped');
+ otherwise writeln('Unspecified error number ',err:3);
+ end; {case}
+ if howbad = fatal
+ then begin
+ writeln('Fatal error ends program at input line',
+ errlin:4);
+ trace(sysprint);
+ halt;
+ end
+ else writeln('Error at input line',errlin:4);
+ if (not eof(infile)) and (howbad = overlookable)
+ then begin
+ readln(infile);
+ writeln('rest of this string has been overlooked');
+ end;
+ errlin := 0; {reset default line to lineno}
+ end; {Error}
+
+@
+@p procedure skipblanks;
+begin
+ while (infile@@=' ') and not eoln(infile) do
+ get(infile); {advance one character}
+ end;
+
+@
+@<Global Types@>=
+@!chrset = set of char;
+
+@ This procedure reads a string from infile to be delimited by
+a character in the set delimiters.
+
+@p procedure readmnemonic(var mn :string(40);
+ delimiters :chrset);
+var
+ str :string(1);
+begin
+ mn := '';
+ while (length(mn) <= 40) and not (infile@@ in delimiters) do begin
+ read(infile,str);
+{writeln('character="',str);}
+ mn := mn || str;
+ end;
+ errstr := mn; {save globally for error messages}
+ end; {readmnemonic}
+
+@
+@<Global Constants@>=
+ exspacekey = 'EXTRASPACE';
+ quadkey = 'QUAD';
+ slantkey = 'SLANT';
+ spaceekey = 'SPACE';
+ spshrkey = 'SPSHRINK';
+ spstrkey = 'SPSTRETCH';
+ xhtkey = 'XHEIGHT';
+
+@
+@p procedure printparam(p :parameter);
+begin
+ case p of
+ slant :write(slantkey:5);
+ spacee :write(spaceekey:6);
+ spstretch :write(spstrkey:9);
+ spshrink :write(spshrkey:8);
+ xheight :write(xhtkey:7);
+ quad :write(quadkey:4);
+ extraspace:write(exspacekey:10);
+ otherwise write(' next math parm');
+ end; {case}
+ end; {printparam}
+
+@ This converts the byte to an integer, for further evaluation by
+the program.
+
+@p procedure readinteger(var int :integer);
+var
+ minusint :integer;
+begin
+ skipblanks;
+ if not (infile@@ in ['0'..'9','-']) then error(4,fatal);
+ if infile@@ = '-'
+ then begin
+ get(infile);
+ readinteger(minusint);
+ int := 0 - minusint;
+ end
+ else read(infile,int);
+ end; {readinteger}
+
+@ Reads series of integers separated by * and /. Returns
+ value in fractional form: multiplier/divisor is the real value
+
+@p procedure readexpr(var multiplier :integer;
+ var divisor :integer);
+var
+ op :char;
+ int :integer;
+begin
+ skipblanks;
+ if infile@@ = ' ' then error(4,fatal);
+ op := '*';
+ multiplier := 1;
+ divisor := 1;
+ while not (infile@@ in [' ',',']) do begin
+ readinteger(int);
+ if op = '*'
+ then multiplier := multiplier * int
+ else divisor := divisor * int;
+ if (infile@@ = '*') or (infile@@ = '/')
+ then read(infile,op);
+ end; {while}
+ end; {readexpr}
+
+@
+@p procedure readreal(var r :real);
+var
+ mult :integer;
+ divs :integer;
+begin
+ readexpr(mult,divs);
+ r := (1.0 * mult)/(1.0 * divs);
+ end;
+
+@
+@<Global Constants@>=@/
+ axishtkey = 'AXISHEIGHT';
+ bosp1key = 'BIGOPSPACE1';
+ bosp2key = 'BIGOPSPACE2';
+ bosp3key = 'BIGOPSPACE3';
+ bosp4key = 'BIGOPSPACE4';
+ bosp5key = 'BIGOPSPACE5';
+ codingschemekey = 'CODINGSCHEME';
+ deffontkey = 'DEFFONT';
+ dethicknesskey = 'DEFTHICKNESS';
+ dewidthkey = 'DEFWIDTH';
+ dlims1key = 'DELIMSIZE1';
+ dlims2key = 'DELIMSIZE2';
+ denom1key = 'DENOMLOWER1';
+ denom2key = 'DENOMLOWER2';
+ fntnamekey = 'FONTNAME';
+ hunitskey = 'HUNITS';
+ raizen1key = 'NUMRAISE1';
+ raizen2key = 'NUMRAISE2';
+ raizen3key = 'NUMRAISE3';
+ ptsizekey = 'POINTSIZE';
+ subdrpkey = 'SUBDROP';
+ sub1key = 'SUB1';
+ sub2key = 'SUB2';
+ supdrpkey = 'SUPDROP';
+ sup1key = 'SUP1';
+ sup2key = 'SUP2';
+ sup3key = 'SUP3';
+ vunitskey = 'VUNITS';
+ maxfont = 255;
+ uninitialized = -5000.0;
+
+@
+@<Global Variables@>=
+ codescheme :string(40);
+ def_ft_stuff :driverrec;
+ def_width_stuff :driverrec;
+ fontname :string(40);
+ fontype :fonttypes;
+ hunits :real;
+ params :array[parameter] of real;
+ paramset :array[fonttypes] of set of parameter;
+ pointsize :integer;
+ uniquecode :integer;
+ vunits :real;
+
+@
+@p procedure read_the_parameters;
+label 0;
+var
+ mn :string(40);
+ int :integer;
+ realval :real;
+ mult :integer;
+ divs :integer;
+ isint :boolean;
+ pp :parameter;
+ parmerror :boolean;
+begin
+ while infile@@ in ['A'..'Z','a'..'z','*'] do
+ if (infile@@ = '*')
+ then begin
+ readln(infile);
+ errlin := errlin + 1;
+ end
+ else begin
+ @<read vunits or hunits or parameter@>
+ {You can end the parameters with a '\$' line -- check it}
+ if infile@@ = '$'
+ then begin
+ readln(infile); {Skip end params marker}
+ errlin := errlin + 1;
+ end;
+ {now check that all parameters are given}
+0: parmerror := false;
+ for pp := slant to bgopsp5 do
+ if (params[pp] = uninitialized) and
+ (pp in paramset[fontype]+paramset[standard])
+ then begin
+ write('Parameter ');
+ printparam(pp);
+ writeln(' was not specified');
+ parmerror := true;
+ end;
+ if parmerror then error(7,fatal);
+ end; {|read_the_parameters|}
+
+@
+@<read vunits or hunits or parameter@>=
+ readmnemonic(mn,[' ']);
+ if dump then write('Parameter ',mn);
+ mn := allcaps(mn);
+ if mn = 'ENDPARAMS'
+ @<endparams@>
+ if (mn <> fntnamekey) and (mn <> codingschemekey)
+ then readexpr(mult,divs);
+ if divs = 0
+ then realval := 1.0 * mult
+ else realval := (1.0 * mult)/(1.0 * divs);
+ int := mult;
+ if divs = 1
+ then isint := true
+ else isint := false; {only reals have divisor}
+ if mn = hunitskey
+ @<hunits@>
+ else if mn = vunitskey
+ @<vunits@>
+ else if mn = fntnamekey
+ @<fontname@>
+ else if mn = codingschemekey
+ @<codingscheme@>
+ else if mn = deffontkey
+ @<deffont@>
+ else if mn = dewidthkey
+ @<defwidth@>
+ else if mn = ptsizekey
+ @<pointsize@>
+ else begin
+ @<TEX standard parameter@>
+ readln(infile);
+ errlin := errlin + 1;
+ end; {else..begin}
+
+@
+ @<endparams@>=
+ then begin
+ readln(infile);
+ errlin := errlin + 1;
+ goto 0; {marks end of parameters}
+ end;
+
+@
+ @<hunits@>=
+ then begin
+ hunits := realval;
+ if dump then writeln(' = ',realval);
+ end
+
+@
+ @<vunits@>=
+ then begin
+ vunits := realval;
+ if dump then writeln(' = ',realval);
+ end
+
+@
+ @<fontname@>=
+ then begin
+ skipblanks;
+ readmnemonic(fontname,[' ','.']);
+ fontname := allcaps(fontname);
+ if dump then writeln(' = ',fontname);
+ end
+
+@
+ @<codingscheme@>=
+ then begin
+ skipblanks;
+ readmnemonic(codescheme,[' ','.']);
+ codescheme := allcaps(codescheme);
+ if dump then writeln(' = ',codescheme);
+ end
+
+@
+ @<deffont@>=
+ then {set up |def_ft_stuff| record}
+ with def_ft_stuff do begin
+ cmd := ord(F); {8600 font command}
+ if int > maxfont
+ then error(24,notreallyfatal);
+ code := int * 10;
+ uniquecode := int;
+ if dump then writeln(' = ',int);
+ end
+
+@
+ @<defwidth@>=
+ then {set up |def_width_stuff| record}
+ with def_width_stuff do begin
+ {ratio of set width to point size}
+ cmd := ord(CHWIDTH);
+ param := realval;
+ if dump then writeln(' = ',realval);
+ end
+
+@
+ @<pointsize@>=
+ then begin
+ pointsize := int;
+ if dump then writeln(' = ',int);
+ end
+
+@
+ @<TEX standard parameter@>=
+ if mn = slantkey
+ then pp := slant
+ else if mn = spaceekey
+ then pp := spacee
+ else if mn = spstrkey
+ then pp := spstretch
+ else if mn = spshrkey
+ then pp := spshrink
+ else if mn = xhtkey
+ then pp := xheight
+ else if mn = quadkey
+ then pp := quad
+ else if mn = exspacekey
+ then pp := extraspace
+ @<TEX mathsy parameter@>
+ @<TEX mathex parameter@>
+ else error(1,fatal);
+ params[pp] := realval; {save value of it}
+ if dump then writeln(' = ',realval);
+ if pp in paramset[mathex]
+ then begin
+ if fontype = standard
+ then fontype := mathex
+ else if fontype <> mathex
+ then error(14,fatal);
+ end
+ else if pp in paramset[mathsy]
+ then begin
+ if fontype = standard
+ {change assumption}
+ then fontype := mathsy
+ else if fontype <> mathsy
+ then error(14,fatal);
+ end; {then..begin}
+ end; {else..begin TEX}
+
+@
+ @<TEX mathsy parameter@>=
+ else if mn = raizen1key
+ then pp := raisen1
+ else if mn = raizen2key
+ then pp := raisen2
+ else if mn = raizen3key
+ then pp := raisen3
+ else if mn = denom1key
+ then pp := denom1
+ else if mn = denom2key
+ then pp := denom2
+ else if mn = sup1key
+ then pp := sup1
+ else if mn = sup2key
+ then pp := sup2
+ else if mn = sup3key
+ then pp := sup3
+ else if mn = sub1key
+ then pp := sub1
+ else if mn = sub2key
+ then pp := sub2
+ else if mn = supdrpkey
+ then pp := supdrop
+ else if mn = subdrpkey
+ then pp := subdrop
+ else if mn = dlims1key
+ then pp := dlims1
+ else if mn = dlims2key
+ then pp := dlims2
+ else if mn = axishtkey
+ then pp := axisheight
+
+@
+ @<TEX mathex parameter@>=
+ else if mn = dethicknesskey
+ then pp := defthickness
+ else if mn = bosp1key
+ then pp := bgopsp1
+ else if mn = bosp2key
+ then pp := bgopsp2
+ else if mn = bosp3key
+ then pp := bgopsp3
+ else if mn = bosp4key
+ then pp := bgopsp4
+ else if mn = bosp5key
+ then pp := bgopsp5
+
+@ finds entry in array list if present and returns its index
+ as slot. otherwise it adds it at the end of list (specified
+ by list[-1]) and returns that index as slot.
+
+@p procedure add_to_list(var list :realarray;
+ entry :real;
+ lmax :integer;
+ var slot :integer);
+var
+ linx :integer;
+begin
+ if entry = 0 then begin
+ slot := 0;
+ return;
+ end;
+ for linx := 1 to trunc(list[-1]) do
+ if entry = list[linx]
+ then begin
+ slot := linx;
+ return;
+ end;
+ {not found}
+ if trunc(list[-1]) = lmax then error(5,fatal);
+ list[-1] := list[-1] + 1.0;
+ list[trunc(list[-1])] := entry;
+ slot := trunc(list[-1]);
+ end; {|add_to_list|}
+
+@
+@<Global Variables@>=
+ max :array[comm] of integer;
+ min :array[comm] of integer;
+
+@ Sets up info needed to read driver file info from the input
+ file, and write it in proper format into the driver output file
+
+@p procedure init8600commands;
+var
+ cmd :comm;
+begin
+ for cmd := HDR to MAXCMD do begin {initialize 8600 commands}
+ if cmd in [PS,SS] then begin
+ min[cmd] := 0;
+ max[cmd] := 1275;
+ end
+ else if cmd in [VMF, VMR, RD] then begin
+ min[cmd] := 1;
+ max[cmd] := 1736;
+ end
+ else if cmd in [RW, HML, HMR] then begin
+ min[cmd] := 1;
+ max[cmd] := 14688;
+ end
+ else if cmd in [SL] then begin
+ min[cmd] := 0;
+ max[cmd] := 48;
+ end
+ else if cmd in [RTWT, RTWB] then begin
+ min[cmd] := 0;
+ max[cmd] := 1440;
+ end
+ else if cmd in [F] then begin
+ min[cmd] := 1;
+ max[cmd] := 255;
+ end
+ else begin {catch any others}
+ min[cmd] := 0;
+ max[cmd] := 65535; {largest 2 byte integer}
+ end;
+ end; {FOR..begin}
+ end; {init8600commands}
+
+@
+@<Global Variables@>=
+ depth :realarray;
+ drvdump :boolean;
+ drvpkt :file of driverrec;
+ drvoutcount :integer;
+ dumpval :boolean;
+ height :realarray;
+ inrec :string(30);
+ itcorr :realarray;
+ kernout :realarray;
+ numext :integer;
+ numkerns :integer;
+ numligs :integer;
+ numparams :array[fonttypes] of integer;
+ outstuff :outarray;
+ str1 :string(30);
+ str2 :string(30);
+ terminal :text;
+ width :realarray;
+
+@
+@p procedure initialize;
+var
+ pp :parameter;
+ outinx :integer;
+begin
+ fontname := trim(parms);
+ termout(terminal);
+ termout(output);
+ rewrite(fontpkt,'NAME='||fontname||'.PRTFM.A');
+ rewrite(drvpkt,'NAME='||fontname||'.CGINFO.A');
+ reset(infile,'NAME='||fontname||'.FONTINFO.*');
+ dump := false;
+ drvdump := false;
+{
+ while not eof(terminal) do begin
+ readln(terminal,inrec);
+ inx := index(inrec,'=');
+ if inx < 1
+ then begin
+ errstr := inrec;
+ error(28,notreallyfatal);
+ end
+ else begin
+ str1 := substr(inrec,1,inx-1);
+ str2 := substr(inrec,inx+1,length(inrec)-inx);
+ str1 := allcaps(ltrim(trim(str1)));
+ str2 := allcaps(ltrim(trim(str2)));
+ if str2 = 'false'
+ then dumpval := false
+ else if str2 = 'true'
+ then dumpval := true;
+ if ((str2 <> 'false') and (str2 <> 'true')) or
+ ((str1 <> 'DUMP') and (str1 <> 'DRVDUMP'))
+ then begin
+ errstr := inrec;
+ error(28,notreallyfatal);
+ end
+ else if str1 = 'DUMP'
+ then dump := dumpval
+ else drvdump := dumpval;
+ end;
+ end;
+}
+ hunits := 1.0 / 54.0;
+ vunits := 1.0 / 8.0;
+ lineno := 1;
+ width[-1] := 0;
+ width[0] := 0.0; {for non-existent chars}
+ height[-1] := 0;
+ height[0] := 0.0; {for non-existent chars}
+ depth[-1] := 0;
+ depth[0] := 0.0; {for non-existent chars}
+ itcorr[-1] := 0;
+ itcorr[0] := 0.0; {for non-existent chars}
+ kernout[-1] := 0.0;
+ kernout[0] := 0.0;
+ {Set all tag values to 0 = vanilla}
+ for outinx := 0 to 127 do outstuff[outinx].fourbytes.byte2 := 0;
+ numkerns := -1;
+ numligs := -1;
+ numext := -1;
+ fontname := ' ';
+ codescheme := 'PARC TEXT';
+ fontype := standard;
+ paramset[standard] := [slant..quad,extraspace];
+ paramset[mathsy] := [raisen1..axisheight];
+ paramset[mathex] := [defthickness..bgopsp5];
+ pointsize := 1;
+ numparams[standard] := 7;
+ numparams[mathex] := 13;
+ numparams[mathsy] := 22;
+ for pp := slant to extraspace do params[pp] := uninitialized;
+ drvoutcount := 0; {no records to driver file yet}
+ init8600commands;
+ with def_ft_stuff do begin
+ cmd := ord(F);
+ code := 10; {font 1, fontlet 0 = def}
+ end;
+ with def_width_stuff do begin
+ cmd := ord(CHWIDTH);
+ param := 1.0; {normal width type is def}
+ end;
+ end;{initialize}
+
+@
+@p procedure readentry(var list :realarray;
+ lmax :integer;
+ var slot :integer;
+ units :real);
+var
+ entry :real;
+ multiplier :integer;
+ divisor :integer; {save value in expression}
+begin
+ readexpr(multiplier,divisor);
+ entry := (1.0 * multiplier) / (1.0 * divisor) * units;
+ if dump then writeln('entry read is ',multiplier:4,'/',
+ divisor:4,' * ',units:4:2, '=',entry);
+ add_to_list(list,entry,lmax,slot);
+ end; {readentry}
+
+@
+@<Global Variables@>=
+ lig :array[0..255] of ligrec;
+
+@ The keyword LIGATURE has just been scanned in the input for one of
+characters 0 to 127. Now we expect to find blank separators followed
+by the names of the two characters the ligature replaces. These will
+be separated by a '+' sign. Since the characters in question may
+not have been specified yet, the names will be saved for now, and
+looked up after all 128 character entries have been read.
+
+@
+@p procedure read_ligature_stuff;
+begin
+ skipblanks;
+ numligs := numligs+1;
+ with lig[numligs] do begin
+ sourceline := lineno; {save line for later error messages}
+ readmnemonic(firstname,[' ','+']);
+ if dump then writeln('first char of ligature is ',firstname);
+ if infile@@ <> '+'
+ then error(9,notreallyfatal)
+ else begin
+ get(infile); {skip plus sign}
+ if infile@@ = ' ' then error(10,overlookable);
+ skipblanks; {overlook}
+ readmnemonic(scndname,[' ']); {read second name}
+ if dump then writeln('second character of ligature',
+ ' is ',scndname);
+ end; {else..begin}
+ ligcode := currchar;
+ end; {with..begin}
+ end; {|read_ligature_stuff|}
+
+@
+ @<Global Variables@>=@/
+ charno :charindex;
+
+@ look up this name in the name array and tell where and if it was
+
+@p procedure lookup(lookee :string(40);
+ var charno :charindex; {where if found}
+ var found :boolean); {was it found}
+var
+ inx :integer;
+begin
+ found := false;
+ inx := 0;
+ while not found and (inx<128) do
+ if name[inx] = lookee
+ then found := true {found}
+ else inx := inx + 1; {check next}
+ if found
+ then charno := inx
+ else charno := 127; {not found, avoid out of range err}
+ if not found
+ then begin
+ errstr := lookee; {string for message}
+ error(12,notreallyfatal);
+ end; {then..begin}
+ end; {lookup}
+
+@ Set Tag value in lower 2 bits of byte 2 of outstuff record
+
+@p procedure set_tag_val(chinx :charindex;
+ tagval :integer);
+begin
+ with outstuff[chinx].fourbytes do begin
+ if (byte2 mod 4) <> 0
+ then writeln('Tag for char ',chinx:1,
+ ' already set to ',byte2 mod 4);
+ if tagval > 3 then error(25,fatal);
+ byte2 := (byte2 div 4 * 4) + tagval;
+ end; {with..begin}
+ end; {|set_tag_val|}
+
+@ Convert a real to a "FIX" which is a 32-bit number stored in the
+leftmost 32 bits of a word. The first 12 bits are the integer part, and
+the last 20 are the fraction. The left most bit is the sign
+
+@p function scaleout(r :real) :integer;
+var
+ stri :string(8);
+ int :integer;
+begin
+ int := round(r*'100000'x);
+ if dump then begin
+ stri := hex(int);
+ if length(stri) < 8
+ then stri := substr('00000000',1,8-length(stri)) || stri;
+ writeln('FIX of ',r,' is ',int,', hex value="',stri,'"');
+ end;
+ scaleout := int;
+ end; {scaleout}
+
+@
+@<Global Variables@>=
+ found :boolean;
+ kern :array[0..maxkerns] of kernrec;
+ mnem :string(40);
+
+@ The keyword KERN has been read: process the rest of the line
+
+@p procedure read_kern_stuff;
+begin
+ numkerns := numkerns + 1;
+ skipblanks;
+ with kern[numkerns] do begin
+ readmnemonic(mnem,[' ','+']);
+ lookup(mnem,charno,found);
+ if found
+ then frstchar := charno
+ else begin
+ frstchar := 127;
+ error(12,notreallyfatal);
+ end; {else..begin}
+ if infile@@ = ' '
+ then begin
+ error(9,notreallyfatal);
+ scndchar := 127; {put any old value there}
+ end
+ else begin
+ get(infile); {skip plus sign}
+ if infile@@ = ' '
+ then error(10,overlookable);
+ skipblanks;{overlook it!}
+ readmnemonic(mnem,[' ','-']);
+ lookup(mnem,charno,found);
+ if found
+ then scndchar := charno
+ else scndchar := 127;
+ end; {else..begin}
+ readentry(kernout,maxkerns,kernslot,hunits);
+ end; {with}
+ readln(infile);
+ errlin := errlin + 1;
+ end; {|read_kern_stuff|}
+
+@
+@<Global Constants@>=
+ linktag = 2;
+
+@
+@p procedure read_link_stuff;
+var
+ mn1 :string(40);
+ mn2 :string(40);
+ char1 :charindex;
+ char2 :charindex;
+ found :boolean;
+begin
+ skipblanks;
+ readmnemonic(mn1,[' ']);
+ lookup(mn1,char1,found);
+ skipblanks;
+ readmnemonic(mn2,[' ']);
+ lookup(mn2,char2,found);
+ set_tag_val(char1,linktag); {set tag type for special info}
+ outstuff[char1].fourbytes.byte3 := char2; {next bigger char}
+ readln(infile);
+ errlin := errlin + 1;
+ end;
+
+@
+@<Global Constants@>=
+ maxitcorr = 63;
+
+@
+@p procedure read_mathop_stuff;
+var
+ mn :string(40);
+ charno :charindex;
+ realval :real;
+ found :boolean;
+ slot :integer; {index into itcorr array for kerns}
+ tag :integer; {saves "tag" value from outstuff array}
+begin
+ skipblanks;
+ readmnemonic(mn,[' ']);
+ lookup(mn,charno,found);
+ readreal(realval);
+ add_to_list(itcorr,realval,maxitcorr,slot); {add value to list}
+ with outstuff[charno].fourbytes do begin
+ tag := byte2 mod 4; {tag is lower 2 bits}
+ byte2 := slot * 4 + tag; {and ic index is upper 6 bits}
+ end;{with..begin}
+ readln(infile);
+ errlin := errlin + 1;
+ end; {|read_mathop_stuff|}
+
+@
+@<Global Constants@>=
+ botkey = 'BOT';
+ extkey = 'EXT';
+ midkey = 'MID';
+ topkey = 'TOP';
+ exttag = 3;
+
+@
+@<Global Variables@>=
+ ext :array[0..127] of extrec;
+
+@ Name and keyword BUILD already read. Now expect keyword=value,
+keyword=value, ... Keywords are TOP, MID, BOT, EXT, and values are
+char names. Add a miscellaneous entry and point to it from this
+character record. initialize the four names in the ext record
+and set the four character values to zero. Later we will look up the
+names to determine the actual character numbers of the components.
+
+@p procedure read_build_stuff;
+var
+ key :string(40);
+ charname :string(40);
+begin
+ numext := numext + 1;
+ with ext[numext] do begin
+ sourceline := lineno; {save line for later error messages}
+ top := 0;
+ mid := 0;
+ bot := 0;
+ ext := 0;
+ topname := '';
+ midname := '';
+ botname := '';
+ extname := '';
+ end;
+ set_tag_val(currchar,exttag);
+ outstuff[currchar].fourbytes.byte3 := numext; {ptr into EXT array}
+ skipblanks;
+ repeat
+ if infile@@ = ',' then get(infile); {skip separator}
+ readmnemonic(key,['=']);
+ key := allcaps(key);
+ get(infile); {skip equal sign delimiter}
+ readmnemonic(charname,[',',' ']);
+ if key = topkey then ext[numext].topname := charname
+ else if key = midkey then ext[numext].midname := charname
+ else if key = botkey then ext[numext].botname := charname
+ else if key = extkey then ext[numext].extname := charname
+ else error(13,fatal);
+ until infile@@ <> ',';
+ end; {|read_build_stuff|}
+
+@
+@<Global Variables@>=
+ drvrec :driverrec;
+
+@ Print out the record about to be written to the driver file. This can
+ be turned on or off by setting constant 'drvdump' to true or false.
+
+@p procedure dump_driver_rec(cmdord :integer);
+begin
+ with drvrec do begin
+ if drvoutcount = 0
+ then writeln('*** Driver File Output ***');
+ write(drvoutcount:3,': ');
+ drvoutcount := drvoutcount + 1;
+ write('Command=',com_table[cmdord],', Parameter=');
+ if (cmdord = ord(CHWIDTH)) or
+ ((cmdord >= ord(VMF)) and (cmdord <= ord(IR))) or
+ (cmdord = ord(RTWT)) or
+ (cmdord = ord(RTWB)) or
+ (cmdord = ord(HMR)) or
+ (cmdord = ord(HML))
+ then writeln(param)
+ else writeln(code);
+ end; {with..begin}
+ end; {|dump_driver_rec|}
+
+@
+@<Global Variables@>=
+ stk :array [0..20] of driverrec;
+
+@ Push a record with an integer character code
+
+@p procedure pushcode(var stktop :integer);
+var
+ icode :integer;
+begin
+ readinteger(icode);
+ if icode > 127 then error(18,notreallyfatal);
+ stktop := stktop + 1;
+ with stk[stktop] do begin
+ cmd := ord(TTS);
+ code := icode;
+ end; {with..begin}
+ end; {pushcode}
+
+@ Reads a command or letter from the input file -- i.e., reads the
+ 1 to 4 char mnemonic and looks it up in array cmdname to find
+ the enumeration value of type "command" corresponding to it.
+ A letter is distinguished from a command because a letter has no
+ parameter whereas all one-character commands do.
+
+@p procedure readcmd(var cmd :comm);
+label 99;
+var
+ name :string(9);
+ ch :string(1);
+ cmdind :comm;
+begin
+ name := '';
+ while infile@@ in ['a'..'z','A'..'Z'] do begin
+ read(infile,ch);
+ name := name || ch;
+ end;
+ name := allcaps(name);
+ errlstr := name; {save name for error message (if needed)}
+ {cmd will be the command or MAXCMD if not found}
+ for cmdind := HDR to MAXCMD do
+ if name = com_table[ord(cmdind)] then goto 99;
+99: cmd := cmdind;
+ if cmdind = MAXCMD then error(19,notreallyfatal); {bad command}
+ end; {readcmd}
+
+@ Read the command "cmnd" -- see if it has any parameters, and
+if so read them. Push a record on the local stack for this command
+
+@p procedure readparams(var stktop :integer);
+var
+ cmnd :comm;
+begin
+ readcmd(cmnd);
+ stktop := stktop + 1; {push}
+ with stk[stktop] do begin
+ cmd := ord(cmnd);
+ if cmnd in [AU] {this one has no parameters}
+ then code := 0
+ else begin {read integer parm, stuff it into record}
+ if cmnd in [IR,VMF,VMR,RW,RD,RTWT,RTWB,HMR,HML]
+ then begin
+ readreal(param);
+ if (round(param) < min[cmnd]) or
+ (round(param) > max[cmnd]) then begin
+ errlstr := com_table[ord(cmnd)];
+ errnum := round(param);
+ errmax := max[cmnd];
+ errmin := min[cmnd];
+ error(17,notreallyfatal);
+ param := min[cmnd];
+ end; {then..begin}
+ if cmnd in [VMF,VMR,RD]
+ then param := param * vunits
+ else param := param * hunits;
+ end {then..end}
+ else begin
+ readinteger(code);
+ if (code < min[cmnd]) or
+ (code > max[cmnd]) then begin
+ errlstr := com_table[ord(cmnd)];
+ errnum := code;
+ errmax := max[cmnd];
+ errmin := min[cmnd];
+ error(17,notreallyfatal);
+ code := min[cmnd];
+ end; {then..begin}
+ end; {else..begin}
+ end; {else..begin}
+ end; {with..begin}
+ end; {readparams}
+
+@* Read 8600 Codes.
+The 8600 code info corresponding to this character will consist of 1
+or more entries separated by commas and containing no blanks. An entry
+can be a decimal character code or an 8600 command in which case it
+begins with a letter. The command name will be up to 4 letters long and
+may be followed by a parameter. Some commands take integer parameters
+and others take reals which will be given in the same format as widths,
+etc: i.e., as products and quotients of integer values.
+Following are listed the possible codes.
+\settabs\+\indent&RTWB = &Reverse Type Window Bottom\quad&value=0--1440
+in 10ths of a point\cr
+\+&PS\hfill =&Point Size\hfill&value=0--1275 in 10ths of a point.\cr
+\+&SS\hfill =&Set Size\hfill&value=0--1275 in 10ths of a point.\cr
+\+&VMF\hfill =&Vertical Move Forward\hfill&value=1--1736 in 8ths of a
+point\cr
+\+&VMR\hfill =&Vertical Move Reverse\hfill&value=1--1736 in 8ths of a
+point\cr
+\+&RD\hfill =&Rule Depth\hfill&value=1--1736 in 8ths of a point\cr
+\+&RW\hfill =&Rule Width\hfill&value=1--1736 in 8ths of a point\cr
+\+&HML\hfill =&Horizontal Move Left\hfill&value=1--14688 in 18ths of a
+point\cr
+\+&HMR\hfill =&Horizontal Move Right\hfill&value=1--14688 in 18ths of a
+point\cr
+\+&SL\hfill =&Slant\hfill&value=0 or 48\cr
+\+&IR\hfill =&Insert Rule\cr
+\+&AU\hfill =&Auxiliary Font\cr
+\+&F\hfill =&Font\hfill&value=1--255\cr
+\+&RTWT\hfill =&Reverse Type Window Top\hfill&value=0--1440 in 16ths of
+a point\cr
+\+&RTWB =&Reverse Type Window Bottom&value=0--1440 in 16ths of a point\cr
+
+@p procedure read8600codes;
+var
+ more_to_come :boolean;
+ stktop :integer; {gives index of last used (initially 0)}
+ inx :integer;
+begin
+ skipblanks;
+ stktop := -1; {no code yet}
+ if eoln(infile)
+ then error(20,notreallyfatal) {no code given}
+ else begin
+ {Repeat until next is no longer a comma, in which case it
+ had better be a blank!}
+ repeat
+ if infile@@ in ['0'..'9']
+ then pushcode(stktop) {decimal character code}
+ else if infile@@ in ['A'..'Z','a'..'z']
+ {read command and optional parms}
+ then readparams(stktop)
+ else error(21,notreallyfatal);
+ if infile@@ = ','
+ then begin
+ more_to_come := true;
+ get(infile);
+ end {then..begin}
+ else more_to_come := false;
+ until not more_to_come;
+ end; {else..begin}
+ {put header record out}
+ drvrec.cmd := ord(HDR);
+ drvrec.code := stktop; {how many codes for this character}
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ {write out character width record}
+ drvrec.cmd := ord(CHWIDTH);
+ drvrec.param := width[outstuff[currchar].fourbytes.byte0];
+ {nw for the current character indexes its width in points}
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ {Put out rec for each code or command}
+ for inx := 0 to stktop do begin
+ drvrec := stk[inx];
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ end; {FOR..begin}
+ end; {read8600codes}
+
+@* MAIN PROGRAM.
+
+@<Global Constants@>=
+ buildkey = 'BUILD';
+ kernkey = 'KERN';
+ ligaturekey = 'LIGATURE';
+ linkkey = 'LINK';
+ mathopkey = 'MATHOP';
+ maxdp = 15;
+ maxht = 15;
+ maxwd = 255;
+ ligkerntag = 1;
+
+@
+@<Global Variables@>=
+ chnum :0..127;
+ chrt :char;
+ first :boolean;
+ heightslot :integer;
+ iny :integer;
+ itc :real;
+ ligcnt :integer;
+ ligout :array[0..255] of memoryword;
+ pp :parameter;
+ slot :integer;
+ tag :integer;
+
+@
+@p begin
+ {--------------- initialize -----------------------}
+ initialize;
+ {---------------- read input -------------------------}
+ read_the_parameters;
+ {Write first three entries to driver file}
+ drvrec.cmd := ord(HDR);
+ drvrec.code := ord(MAXCMD);
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ drvrec := def_ft_stuff;
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ drvrec := def_width_stuff;
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ drvrec.cmd := ord(SL);
+ if params[slant] = uninitialized
+ then drvrec.code := 0
+ else drvrec.code := trunc(params[slant]);
+ if drvdump then dump_driver_rec(drvrec.cmd);
+ drvpkt@@ := drvrec;
+ put(drvpkt);
+ {Now read entries...}
+ for currchar := 0 to num_entries-1 do begin
+ if eof(infile) then error(7,fatal);
+ with outstuff[currchar].fourbytes do
+ byte3 := 0; {set remainder to 0}
+ readmnemonic(mnem,[' ']); {if any}
+ if dump then writeln('** Processing font info for "',mnem,'"');
+ name[currchar] := mnem; {save for later use!}
+ {one non-blank character -- check sequencing}
+ readstr(mnem,chrt);
+ if (length(mnem) = 1) and (ordx[chrt] <> currchar)
+ then error(6,notreallyfatal);
+ skipblanks; {look at next non-blank}
+ if infile@@ in ['A'..'Z','a'..'z']
+ then begin
+ readmnemonic(mnem,[' ']);
+ mnem := allcaps(mnem);
+ if mnem = ligaturekey
+ then read_ligature_stuff
+ else if mnem = buildkey
+ then read_build_stuff
+ else error(16,fatal);
+ end; {then..begin}
+ readentry(width,maxwd,slot,hunits); {read width}
+ outstuff[currchar].fourbytes.byte0 := slot; {save wid index}
+ readentry(height,maxht,slot,vunits); {read height}
+ heightslot := slot; {remember ht slot until depth read}
+ itc := height[slot] * params[slant]; {it corr = ht * slant}
+ add_to_list(itcorr,itc,maxitcorr,slot);
+ with outstuff[currchar].fourbytes do begin
+ tag := byte2 mod 4; {save tag value}
+ byte2 := slot * 4 + tag; {put itcorr in 6 bits, tag in 2}
+ end;
+ readentry(depth,maxdp,slot,vunits); {read depth}
+ outstuff[currchar].fourbytes.byte1 := heightslot * 16 + slot;
+ {now read 8600 information...}
+ read8600codes;
+ if not eoln(infile)
+ then begin
+ readln(infile);
+ errlin := errlin + 1;
+ end;
+ end; {FOR..begin}
+ {character entries have all been read...remaining entries
+ must begin with a keyword: KERN, Link, MATHOP These supply
+ additional information about certain characters}
+ while not eof(infile) do begin
+ readmnemonic(mnem,[' ']);
+ mnem := allcaps(mnem);
+ if mnem = kernkey
+ then read_kern_stuff
+ else if mnem = linkkey
+ then read_link_stuff
+ else if mnem = mathopkey
+ then read_mathop_stuff
+ else error(11,fatal);
+ end; {while..begin}
+@<look up lignames@>@/
+@<set up ligature info@>@/
+@<write font file@>@/
+end.
+
+@
+@<look up lignames@>=
+ for inx:= 0 to numligs do
+ with lig[inx] do begin
+ errlin := sourceline;
+ lookup(firstname,frstchar,found);
+ if not found
+ then begin
+ errlin := sourceline;
+ error(12,notreallyfatal);
+ frstchar := 127;
+ end; {then..begin}
+ lookup(scndname,scndchar,found);
+ if not found
+ then begin
+ errlin := sourceline;
+ error(12,notreallyfatal);
+ scndchar := 127;
+ end; {then..begin}
+ end; {with..begin}
+ for inx := 0 to numext do
+ with ext[inx] do begin
+ errlin := sourceline;
+ if topname <> '' then lookup(topname,top,found);
+ if midname <> '' then lookup(midname,mid,found);
+ if botname <> '' then lookup(botname,bot,found);
+ if extname <> '' then lookup(extname,ext,found);
+ end; {with..begin}
+
+@
+@<set up ligature info@>=
+ ligcnt := 0;
+ for chnum := 0 to 127 do begin
+ first := true;
+ for inx := 0 to numligs do
+ if lig[inx].frstchar = chnum
+ then begin
+ if first then begin
+ {ptr to first ligature}
+ outstuff[chnum].fourbytes.byte3 := ligcnt;
+ set_tag_val(chnum,ligkerntag);
+ first := false;
+ end; {then..begin}
+ with ligout[ligcnt].fourbytes, lig[inx] do
+ begin {transfer info into output records}
+ {byte0=1 if last ligature
+ byte1=next chararacter of ligature
+ byte2=0 for a ligature
+ byte3=code of ligature character}
+ byte0 := '00000000'B;
+ byte1 := scndchar;
+ byte2 := '00000000'B;
+ byte3 := ligcode;
+ end; {with..begin}
+ ligcnt := ligcnt+1; {ready for next one}
+ end; {then..begin}
+ {Now do same with kerns}
+ for inx := 0 to numkerns do
+ if kern[inx].frstchar = chnum
+ then begin
+ if first then begin
+ {link to ligature record}
+ outstuff[chnum].fourbytes.byte3 := ligcnt;
+ set_tag_val(chnum,ligkerntag);
+ first := false; {not anymore}
+ end; {then..begin}
+ with ligout[ligcnt].fourbytes, kern[inx] do
+ begin {transfer info into output records}
+ {byte0=1 if last kern
+ byte1=next chararacter of kern
+ byte2=1 for a kern
+ byte3=index of kern character}
+ byte0 := '00000000'B;
+ byte1 := scndchar;
+ byte2 := '10000000'B;
+ byte3 := kernslot;
+ end; {with..begin}
+ ligcnt := ligcnt + 1;
+ end; {FOR,then..begin}
+ if not first
+ then ligout[ligcnt-1].fourbytes.byte0 := '10000000'B;
+ {there was at least one record, so flag the last of 'em}
+ end; {FOR..begin} {that's all the characters}
+
+@ Now write out in TEX format. TUGboat vol 2, no 1 article
+ TEX FONT METRIC fileS says the first 12 half-words of the
+ file are lengths, and obey the following equation:
+ lenfile=6 + lh + (ec-be+1) + nw + nh + nd + ni + nk + nl + ne + np
+ lh=length of header=18 words ec=end character=127,
+ bc=beginning char=0, hence:
+ lenfile = 6 + 18 + 128 + nw + nh + nd + ni + nk + nl + ne + np
+ Calculate its value:
+
+@<write font file@>=
+ if not eof(infile) then error(7,fatal);
+ lenfile := 6 + 18 + 128 + trunc(width[-1]) + trunc(height[-1])@/
+ + trunc(depth[-1]) + trunc(itcorr[-1])@/
+ + numkerns + numligs + numext + trunc(kernout[-1])@/
+ + numparams[fontype] {num parameters}@/
+ + 8; {off by one on nw,nh,nd,ni,nk,nl,ne and kernout}@/
+ if dump then writeln('Writing info to TEX-readable file');
+ outcount := 0; {start counting}
+ writeln('Number of records is ',lenfile:4);
+ if dump then write('* lf, lh:');
+ outrec.twohalves.lhword := lenfile; {len of file}
+ outrec.fourbytes.rhword := 18; {len of header}
+ writeout(outrec,3);
+ if dump then write('* bc, ec:');
+ outrec.twohalves.lhword := 0; {first char code}
+ outrec.twohalves.rhword := 127; {last char code}
+ writeout(outrec,3);
+ if dump then write('* nw, nh:');
+ outrec.twohalves.lhword := trunc(width[-1]+1);
+ outrec.twohalves.rhword := trunc(height[-1]+1);
+ writeout(outrec,3);
+ if dump then write('* nd, ni:');
+ outrec.twohalves.lhword := trunc(depth[-1]+1);
+ outrec.twohalves.rhword := trunc(itcorr[-1]+1);
+ writeout(outrec,3);
+ if dump then write('* nl, nk:');
+ outrec.twohalves.lhword := numligs + numkerns + 2;
+ outrec.twohalves.rhword := trunc(kernout[-1]+1);
+ writeout(outrec,3);
+ if dump then write('* ne, np:');
+ outrec.twohalves.lhword := numext+1;
+ outrec.twohalves.rhword := numparams[fontype];
+ writeout(outrec,3);
+
+ if dump then writeln('*** Header info');
+ outrec.int := uniquecode;
+ writeout(outrec,6);
+ outrec.int := scaleout(float(pointsize)); {def = 1 point}
+ writeout(outrec,2);
+ writeln('*** Coding Scheme = ',codescheme);
+ outrec.fourbytes.byte0 := length(codescheme);
+ for inx := 2 to 40 do begin
+ iny := inx mod 4;
+ if iny = 0 then iny := 4;
+ iny := iny - 1;
+ if inx-1 <= length(codescheme)
+ then readstr(substr(codescheme,inx-1,1),chrt)
+ else chrt := chrx['00'X];
+ with outrec.fourbytes do case iny of
+ 0 :byte0 := ordx[chrt];
+ 1 :byte1 := ordx[chrt];
+ 2 :byte2 := ordx[chrt];
+ 3 :begin
+ byte3 := ordx[chrt];
+ writeout(outrec,4);
+ end;
+ otherwise error(99,fatal);
+ end;
+ end;
+ if length(fontname) > 8
+ then begin
+ error(27,notreallyfatal);
+ fontname := substr(fontname,1,8);
+ end;
+ writeln('*** Font Name = ',fontname);
+ outrec.fourbytes.byte0 := length(fontname);
+ for inx := 2 to 20 do begin
+ iny := inx mod 4;
+ if iny = 0 then iny := 4;
+ iny := iny - 1;
+ if inx-1 <= length(fontname)
+ then readstr(substr(fontname,inx-1,1),chrt)
+ else chrt := chrx['00'X];
+ with outrec.fourbytes do case iny of
+ 0 :byte0 := ordx[chrt];
+ 1 :byte1 := ordx[chrt];
+ 2 :byte2 := ordx[chrt];
+ 3 :begin
+ byte3 := ordx[chrt];
+ writeout(outrec,4);
+ end;
+ otherwise error(99,fatal);
+ end;
+ end;
+ outrec.int := 0;
+ writeout(outrec,6); {no Parc face byte now}
+ @<Write out FINFO part of font file@>
+ @<write out widths@>
+ @<write out heights@>
+ @<write out depths@>
+ @<write out itcorrs@>
+ @<write out ligature/kern programs@>
+ @<write out extension chars@>
+ @<write params@>
+ writeln('End of font file preprocessor');
+
+@
+ @<Write out FINFO part of font file@>=
+ for inx := 0 to num_entries-1 do writeout(outstuff[inx],5);
+
+@
+ @<write out widths@>=
+ if dump then writeln('*** Widths');
+ for inx := 0 to trunc(width[-1]) do begin
+ outrec.int := scaleout(width[inx]);
+ writeout(outrec,2);
+ end;
+
+@
+ @<write out heights@>=
+ if dump then writeln('*** Heights');
+ for inx := 0 to trunc(height[-1]) do begin
+ outrec.int := scaleout(height[inx]);
+ writeout(outrec,2);
+ end;
+
+@
+ @<write out depths@>=
+ if dump then writeln('*** Depths');
+ for inx := 0 to trunc(depth[-1]) do begin
+ outrec.int := scaleout(depth[inx]);
+ writeout(outrec,2);
+ end;
+
+@
+ @<write out itcorrs@>=
+ if dump then writeln('*** itcorrs');
+ for inx := 0 to trunc(itcorr[-1]) do begin
+ outrec.int := scaleout(itcorr[inx]);
+ writeout(outrec,2);
+ end;
+
+@
+ @<write out ligature/kern programs@>=
+ if dump then writeln('*** Lig/Kern programs');
+ for inx := 0 to numligs + numkerns + 1 do
+ writeout(ligout[inx],4);
+ {write out kern values as fixes}
+ if dump then writeln('*** Kern values');
+ for inx := 0 to trunc(kernout[-1]) do begin
+ outrec.int := scaleout(kernout[inx]);
+ writeout(outrec,2);
+ end;
+
+@
+ @<write out extension chars@>=
+ if dump then writeln('*** Extension chars');
+ for inx := 0 to numext do
+ with ext[inx], outrec.fourbytes do begin
+ byte0 := top;
+ byte1 := mid;
+ byte2 := bot;
+ byte3 := ext;
+ writeout(outrec,4);
+ end; {with..begin}
+
+@
+ @<write params@>=
+ if dump then writeln('*** Params');
+ for pp := slant to bgopsp5 do
+ if pp in paramset[standard]+paramset[fontype]
+ then begin
+ if dump then begin
+ printparam(pp);
+ write(': ');
+ end;
+ outrec.int := scaleout(params[pp]);
+ writeout(outrec,2);
+ end; {then..begin}
+
+@* INDEX.