@x
{hz int_pars go here}
@y
@d hz_state_code=80
@z

@x
@d error_context_lines==int_par(error_context_lines_code)
@y
@d hz_state==int_par(hz_state_code)
@d hz_en==(hz_state>0)
@d error_context_lines==int_par(error_context_lines_code)
@z

@x
error_context_lines_code:print_esc("errorcontextlines");
@y
error_context_lines_code:print_esc("errorcontextlines");
hz_state_code:print_esc("hzstate");
@z

@x
primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
@y
primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
primitive("hzstate",assign_int,int_base+hz_state_code);@/
@!@:hz_state_}{\.{\\hzstate} primitive@>
@z

@x
escape_char:="\"; end_line_char:=carriage_return;
@y
escape_char:="\"; end_line_char:=carriage_return;
hz_state:=0;
@z

@x
primitive("font",def_font,0);@/
@!@:font_}{\.{\\font} primitive@>
@y
primitive("font",def_font,0);@/
@!@:font_}{\.{\\font} primitive@>
primitive("fontvariant",def_font,1);@/
@!@:fontvariant_}{\.{\\fontvariant} primitive@>
@z

@x
def_font: print_esc("font");
@y
def_font: if chr_code=0 then print_esc("font") else print_esc("fontvariant");
@z

@x l.8326
@p procedure scan_something_internal(@!level:small_number;@!negative:boolean);
 {fetch an internal parameter}
var m:halfword; {|chr_code| part of the operand token}
@y
@p procedure scan_something_internal(@!level:small_number;@!negative:boolean);
 {fetch an internal parameter}
var m:halfword; {|chr_code| part of the operand token}
r:pointer; {used with font variants}
@z

@x l.8379
else  begin back_input; scan_font_ident;
 scanned_result(font_id_base+cur_val)(ident_val);
 end
@y
else begin
 if m=0 then begin
   back_input; scan_font_ident;
   scanned_result(font_id_base+cur_val)(ident_val);
 end else if hz_en then begin
   scan_font_ident;
   r:=font_variants[cur_val]; cur_val:=0;
   while r<>null do begin
     incr(cur_val); r:=link(r);
   end;
   scanned_result(cur_val)(int_val);
 end else begin
   print_err("Improper "); print_cmd_chr(def_font,m);
   error;
 end;
end
@z

@x l.10710
@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
 {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
@y
@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
 {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
@!font_variants:array[internal_font_number] of min_halfword..max_halfword;
@z

@x l.10762
for k:=0 to 6 do font_info[k].sc:=0;
@y
for k:=0 to 6 do font_info[k].sc:=0;
font_variants[null_font]:=null;
@z

@x l.11189
fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f; goto done
@y
fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f;
for a:=0 to 255 do begin
 qw:=char_info(f)(a);
 if char_exists(qw) then begin
   font_variants[f]:=get_avail;
   font(font_variants[f]):=f;
   character(font_variants[f]):=a;
   link(font_variants[f]):=null;
   goto done;
 end;
end;
goto done;
@z

@x l.12860
@ Here now is |hpack|, which contains few if any surprises.

@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer;
label reswitch, common_ending, exit;
var r:pointer; {the box node that will be returned}
@!q:pointer; {trails behind |p|}
@!h,@!d,@!x:scaled; {height, depth, and natural width}
@y
@ This routine replaces characters in the hlist |p| with variants from other
fonts in order to stretch the natural width by |r|. It returns the amount by
which the natural width could be stretched.

@p function adjust_excess(p:pointer;r:real):scaled;
label reswitch,found,done;
var v,w,d,dd:scaled;
f,ff,c:eight_bits;
i,ii:four_quarters;
s:pointer;
j:integer;
begin
@!debug
print("(adjust_excess: "); print_scaled(round(unity*r));
print(" --> ");
gubed
v:=0; w:=0;
while p<>null do begin
 reswitch: while is_char_node(p) do begin
   c:=character(p); f:=font(p); i:=char_info(f)(c);
   w:=w+char_width(f)(i);
   s:=font_variants[f];
   f:=font(s); i:=char_info(f)(c);
   d:=abs(v+char_width(f)(i)-r*w);
   s:=link(s);
@!debug
   j:=0;
gubed
   while s<>null do begin
     ff:=font(s); ii:=char_info(ff)(c);
     dd:=abs(v+char_width(ff)(ii)-r*w);
     if dd>=d then goto found;
     d:=dd; f:=ff; i:=ii;
@!debug
     j:=j+1;
gubed
     s:=link(s);
   end;
   found:
   font(p):=f;
@!debug
   print_int(j);
gubed
   v:=v+char_width(f)(i);
   p:=link(p);
 end;
 if p=null then goto done;
 if type(p)=ligature_node then
   @<Make node |p| look like a |char_node| and |goto reswitch|@>
 else p:=link(p);
end;
done:
@!debug
print_ln;
print(" got "); print_scaled(v-w); print(")");
gubed
adjust_excess:=v-w;
end;

@ Here now is |hpack|, which contains few if any surprises.

@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer;
label reswitch, common_ending, exit;
var r:pointer; {the box node that will be returned}
@!q:pointer; {trails behind |p|}
@!h,@!d,@!x:scaled; {height, depth, and natural width}
@!gw:scaled; {natural width coming from glyphs}
@z

@x
h:=0; @<Clear dimensions to zero@>;
@y
h:=0; gw:=0; @<Clear dimensions to zero@>;
@z

@x
@<Incorporate character dimensions into the dimensions of the hbox...@>=
begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
x:=x+char_width(f)(i);@/
@y
@<Incorporate character dimensions into the dimensions of the hbox...@>=
begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
x:=x+char_width(f)(i);@/
gw:=gw+char_width(f)(i);
@z

@x
else if x>0 then @<Determine horizontal glue stretch setting, then |return|
   or \hbox{|goto common_ending|}@>
else @<Determine horizontal glue shrink setting, then |return|
   or \hbox{|goto common_ending|}@>
@y
else begin
 if hz_en then begin
   if gw<>0 then begin
     x:=x-adjust_excess(link(q),(gw+x)/gw);
   end;
 end;
 if x>0 then @<Determine horizontal glue stretch setting, then |return|
   or \hbox{|goto common_ending|}@>
 else @<Determine horizontal glue shrink setting, then |return|
   or \hbox{|goto common_ending|}@>;
end
@z

@x
@d delta_node_size=7 {number of words in a delta node}
@d delta_node=2 {|type| field in a delta node}
@y
@d delta_node_size=9 {number of words in a delta node}
@d delta_node=2 {|type| field in a delta node}
@z

@x
@d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)

@<Glo...@>=
@!active_width:array[1..6] of scaled;
 {distance from first active node to~|cur_p|}
@!cur_active_width:array[1..6] of scaled; {distance from current active node}
@!background:array[1..6] of scaled; {length of an ``empty'' line}
@!break_width:array[1..6] of scaled; {length being computed after current break}
@y
For the hz algorithm, we add two more fields to store the finite
stretch and shrink from glyphs.

@d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)
@d do_all_eight(#)==do_all_six(#);#(7);#(8)

@<Glo...@>=
@!active_width:array[1..8] of scaled;
 {distance from first active node to~|cur_p|}
@!cur_active_width:array[1..8] of scaled; {distance from current active node}
@!background:array[1..8] of scaled; {length of an ``empty'' line}
@!break_width:array[1..8] of scaled; {length being computed after current break}
@z

@x
background[6]:=shrink(q)+shrink(r);
@y
background[6]:=shrink(q)+shrink(r);
background[7]:=0; background[8]:=0;
@z

@x
do_all_six(copy_to_cur_active);
@y
do_all_eight(copy_to_cur_active);
@z

@x
 begin do_all_six(update_width);
@y
 begin do_all_eight(update_width);
@z

@x
begin no_break_yet:=false; do_all_six(set_break_width_to_background);
@y
begin no_break_yet:=false; do_all_eight(set_break_width_to_background);
@z

@x
 begin do_all_six(convert_to_break_width);
@y
 begin do_all_eight(convert_to_break_width);
@z

@x
 begin do_all_six(store_break_width);
@y
 begin do_all_eight(store_break_width);
@z

@x
 do_all_six(new_delta_to_break_width);
@y
 do_all_eight(new_delta_to_break_width);
@z

@x
 do_all_six(new_delta_from_break_width);
@y
 do_all_eight(new_delta_from_break_width);
@z

@x
shortfall:=line_width-cur_active_width[1]; {we're this much too short}
@y
if hz_en then begin
 if cur_active_width[1]+cur_active_width[7]<line_width then
   shortfall:=line_width-(cur_active_width[1]+cur_active_width[7])
 else if cur_active_width[1]-cur_active_width[8]>line_width then
   shortfall:=line_width-(cur_active_width[1]-cur_active_width[8])
 else shortfall:=0;
end else shortfall:=line_width-cur_active_width[1]; {we're this much too short}
@z

@x
   begin do_all_six(downdate_width);
@y
   begin do_all_eight(downdate_width);
@z

@x
   begin do_all_six(update_width);
   do_all_six(combine_two_deltas);
@y
   begin do_all_eight(update_width);
   do_all_eight(combine_two_deltas);
@z

@x
 begin do_all_six(update_active);
 do_all_six(copy_to_cur_active);
@y
 begin do_all_eight(update_active);
 do_all_eight(copy_to_cur_active);
@z

@x
do_all_six(store_background);@/
@y
do_all_eight(store_background);@/
@z

@x
begin prev_p:=cur_p;
repeat f:=font(cur_p);
act_width:=act_width+char_width(f)(char_info(f)(character(cur_p)));
cur_p:=link(cur_p);
until not is_char_node(cur_p);
end
@y
begin prev_p:=cur_p;
repeat f:=font(cur_p);
w:=char_width(f)(char_info(f)(character(cur_p)));
act_width:=act_width+w;
if hz_en then @<Update stretch and shrink in the presence of font variants@>;
cur_p:=link(cur_p);
until not is_char_node(cur_p);
end

@ @<Local variables for line...@>=
@!w:scaled; {used when calculating character widths}

@ @<Update stretch and shrink...@>=
begin
r:=font_variants[f];
active_width[7]:=active_width[7]+
          char_width(font(r))(char_info(font(r))(character(cur_p)))-w;
while link(r)<>null do r:=link(r);
active_width[8]:=active_width[8]+
          w-char_width(font(r))(char_info(font(r))(character(cur_p)));
end
@z

@x
def_font: new_font(a);
@y
def_font: if cur_chr=0 then new_font(a) else
 if hz_en then font_variant(a)
 else begin print_err("Improper "); print_cmd_chr(def_font,cur_chr); error; end;


@ @<Declare subprocedures for |prefixed_command|@>=
procedure font_variant(@!a:small_number);
label found,done;
var @!f,ff:internal_font_number;
@!i:eight_bits;
@!qw,qww:four_quarters;
@!r,s,p: pointer;
begin
 scan_font_ident; f:=cur_val;
 scan_optional_equals; scan_font_ident; ff:=cur_val;
 @<If |ff| is |null_font|, reset the list of variants and goto |found|@>;
 @<Check that |ff| is a valid variant of |f|, else goto |done|@>;
 s:=null; r:=font_variants[f];
 i:=character(r);
 p:=new_character(ff,i);
 qww:=char_info(ff)(i);
 while r<>null do begin
   if font(r)=ff then goto done;
   qw:=char_info(font(r))(i);
   if char_width(font(r))(qw)<char_width(ff)(qww) then goto found;
   s:=r; r:=link(r);
 end;
found:
link(p):=r;
if s=null then font_variants[f]:=p
else link(s):=p;
done:
@!debug
print("(fontvariants of ");
print(font_name[f]); print(": ");
r:=font_variants[f];
while r<>null do begin
  print(font_name[font(r)]);
  r:=link(r);
  if r<>null then print(", ");
end;
print(")");
gubed
end;

@ Fonts can only be variants of each other if they provide the
same characters.

@<Check that |ff| is a valid variant of |f|...@>=
for i:=0 to 255 do begin
 qw:=char_info(f)(i);
 qww:=char_info(f)(i);
 if (char_exists(qw) and not char_exists(qww)) or
    (not char_exists(qw) and char_exists(qww)) then begin
   print_err("Font "); print_esc(font_id_text(ff));
   print(" is not a variant of font "); print_esc(font_id_text(f));
@.Font x is not a...@>
   help2("Fonts can only be used as variants of each other")@/
     ("if they contain the same characters.");
   error;
   goto done;
 end;
end

@ The only way to remove variants of a font is to assign |null_font|
as a variant.

@<If |ff| is |null_font|...@>=
if ff=null_font then begin
 r:=null; s:=null; p:=new_character(f,character(font_variants[f]));
 flush_node_list(font_variants[f]);
 goto found;
end
@z