summaryrefslogtreecommitdiff
path: root/systems/tex-extensions/clasen/tight2.patch
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /systems/tex-extensions/clasen/tight2.patch
Initial commit
Diffstat (limited to 'systems/tex-extensions/clasen/tight2.patch')
-rw-r--r--systems/tex-extensions/clasen/tight2.patch1461
1 files changed, 1461 insertions, 0 deletions
diff --git a/systems/tex-extensions/clasen/tight2.patch b/systems/tex-extensions/clasen/tight2.patch
new file mode 100644
index 0000000000..d9a15af1f9
--- /dev/null
+++ b/systems/tex-extensions/clasen/tight2.patch
@@ -0,0 +1,1461 @@
+*** tex.web.orig Mon Sep 14 23:03:05 1998
+--- tex.web Sun Sep 20 10:00:49 1998
+***************
+*** 184,190 ****
+ known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+ November 1984].
+
+! @d banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts}
+
+ @ Different \PASCAL s have slightly different conventions, and the present
+ @!@:PASCAL H}{\ph@>
+--- 184,190 ----
+ known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+ November 1984].
+
+! @d banner=='This is *not* TeX, Version 3.14159' {printed when \TeX\ starts}
+
+ @ Different \PASCAL s have slightly different conventions, and the present
+ @!@:PASCAL H}{\ph@>
+***************
+*** 4951,4957 ****
+ @d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
+ @d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
+ @d error_context_lines_code=54 {maximum intermediate line pairs shown}
+! @d int_pars=55 {total number of integer parameters}
+ @d count_base=int_base+int_pars {256 user \.{\\count} registers}
+ @d del_code_base=count_base+256 {256 delimiter code mappings}
+ @d dimen_base=del_code_base+256 {beginning of region 6}
+--- 4951,4961 ----
+ @d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
+ @d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
+ @d error_context_lines_code=54 {maximum intermediate line pairs shown}
+! @d glue_overshrink_code=55 { 1000 times the amount by which to overshrink}
+! @d extra_adj_demerits_code=56 { additional demerits for jumping > 2 }
+! @d font_demerits_code=57
+! @d extra_font_demerits_code=58
+! @d int_pars=59 {total number of integer parameters}
+ @d count_base=int_base+int_pars {256 user \.{\\count} registers}
+ @d del_code_base=count_base+256 {256 delimiter code mappings}
+ @d dimen_base=del_code_base+256 {beginning of region 6}
+***************
+*** 5014,5019 ****
+--- 5018,5028 ----
+ @d right_hyphen_min==int_par(right_hyphen_min_code)
+ @d holding_inserts==int_par(holding_inserts_code)
+ @d error_context_lines==int_par(error_context_lines_code)
++ @d glue_overshrink==int_par(glue_overshrink_code)
++ @d extra_adj_demerits==int_par(extra_adj_demerits_code)
++ @d font_demerits==int_par(font_demerits_code)
++ @d extra_font_demerits==int_par(extra_font_demerits_code)
++
+
+ @<Assign the values |depth_threshold:=show_box_depth|...@>=
+ depth_threshold:=show_box_depth;
+***************
+*** 5078,5083 ****
+--- 5087,5096 ----
+ right_hyphen_min_code:print_esc("righthyphenmin");
+ holding_inserts_code:print_esc("holdinginserts");
+ error_context_lines_code:print_esc("errorcontextlines");
++ glue_overshrink_code:print_esc("glueovershrink");
++ extra_adj_demerits_code:print_esc("extraadjdemerits");
++ font_demerits_code:print_esc("fontdemerits");
++ extra_font_demerits_code:print_esc("extrafontdemerits");
+ othercases print("[unknown integer parameter!]")
+ endcases;
+ end;
+***************
+*** 5198,5203 ****
+--- 5211,5224 ----
+ @!@:holding_inserts_}{\.{\\holdinginserts} primitive@>
+ primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
+ @!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
++ primitive("glueovershrink",assign_int,int_base+glue_overshrink_code);@/
++ @!@:glue_overshrink_}{\.{\\glueovershrink} primitive@>
++ primitive("extraadjdemerits",assign_int,int_base+extra_adj_demerits_code);@/
++ @!@:extra_adj_demerits_}{\.{\\extraadjdemerits} primitive@>
++ primitive("fontdemerits",assign_int,int_base+font_demerits_code);@/
++ @!@:font_demerits_}{\.{\\fontdemerits} primitive@>
++ primitive("extrafontdemerits",assign_int,int_base+extra_font_demerits_code);@/
++ @!@:extra_font_demerits_}{\.{\\extrafontdemerits} primitive@>
+
+ @ @<Cases of |print_cmd_chr|...@>=
+ assign_int: if chr_code<count_base then print_param(chr_code-int_base)
+***************
+*** 5658,5663 ****
+--- 5679,5686 ----
+ @!@:expand_after_}{\.{\\expandafter} primitive@>
+ primitive("font",def_font,0);@/
+ @!@:font_}{\.{\\font} primitive@>
++ primitive("fontvariant",def_font,1);@/
++ @!@:font_variant_}{\.{\\fontvariant} primitive@>
+ primitive("fontdimen",assign_font_dimen,0);@/
+ @!@:font_dimen_}{\.{\\fontdimen} primitive@>
+ primitive("halign",halign,0);@/
+***************
+*** 5732,5738 ****
+ break_penalty: print_esc("penalty");
+ char_num: print_esc("char");
+ cs_name: print_esc("csname");
+! def_font: print_esc("font");
+ delim_num: print_esc("delimiter");
+ divide: print_esc("divide");
+ end_cs_name: print_esc("endcsname");
+--- 5755,5761 ----
+ break_penalty: print_esc("penalty");
+ char_num: print_esc("char");
+ cs_name: print_esc("csname");
+! def_font: if chr_code=0 then print_esc("font") else print_esc("fontvariant");
+ delim_num: print_esc("delimiter");
+ divide: print_esc("divide");
+ end_cs_name: print_esc("endcsname");
+***************
+*** 8376,8384 ****
+ end;
+ scanned_result(equiv(m))(tok_val);
+ end
+! else begin back_input; scan_font_ident;
+ scanned_result(font_id_base+cur_val)(ident_val);
+! end
+
+ @ Users refer to `\.{\\the\\spacefactor}' only in horizontal
+ mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
+--- 8399,8411 ----
+ end;
+ scanned_result(equiv(m))(tok_val);
+ end
+! else if m=0 then begin
+! back_input; scan_font_ident;
+ scanned_result(font_id_base+cur_val)(ident_val);
+! end else begin
+! print_err("Improper "); print_cmd_chr(def_font, m);
+! error;
+! end
+
+ @ Users refer to `\.{\\the\\spacefactor}' only in horizontal
+ mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
+***************
+*** 10672,10677 ****
+--- 10699,10706 ----
+ @<Types...@>=
+ @!internal_font_number=font_base..font_max; {|font| in a |char_node|}
+ @!font_index=0..font_mem_size; {index into |font_info|}
++ @!four_fonts = packed array[0..3] of quarterword;
++ {a version of |four_quarters| with addressable quarters}
+
+ @ Here now is the (rather formidable) array of font arrays.
+
+***************
+*** 10709,10714 ****
+--- 10738,10745 ----
+ {right boundary character, |non_char| if there is none}
+ @!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 four_fonts;
++ {we use four variants per font}
+
+ @ Besides the arrays just enumerated, we have directory arrays that make it
+ easy to get at the individual entries in |font_info|. For example, the
+***************
+*** 10760,10765 ****
+--- 10791,10797 ----
+ font_glue[null_font]:=null; font_params[null_font]:=7;
+ param_base[null_font]:=-1;
+ for k:=0 to 6 do font_info[k].sc:=0;
++ for k:=0 to 3 do font_variants[null_font][k]:=null_font;
+
+ @ @<Put each...@>=
+ primitive("nullfont",set_font,null_font);
+***************
+*** 12854,12865 ****
+
+ @< Glob...@>=
+ @!adjust_tail:pointer; {tail of adjustment list}
+
+ @ @<Set init...@>=adjust_tail:=null; last_badness:=0;
+
+ @ 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|}
+--- 12886,12905 ----
+
+ @< Glob...@>=
+ @!adjust_tail:pointer; {tail of adjustment list}
++ @!line_font_variant:small_number; {used for communication between
++ |line_break| and |hpack|}
+
+ @ @<Set init...@>=adjust_tail:=null; last_badness:=0;
+
+ @ Here now is |hpack|, which contains few if any surprises.
+
+! @p function effective_glue_overshrink : integer;
+! begin
+! if glue_overshrink<1000 then effective_glue_overshrink:=1000
+! else effective_glue_overshrink:=glue_overshrink;
+! end;
+!
+! 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|}
+***************
+*** 12870,12875 ****
+--- 12910,12916 ----
+ @!f:internal_font_number; {the font in a |char_node|}
+ @!i:four_quarters; {font information about a |char_node|}
+ @!hd:eight_bits; {height and depth indices for a character}
++ @!max_shrink:scaled;
+ begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
+ subtype(r):=min_quarterword; shift_amount(r):=0;
+ q:=r+list_offset; link(q):=p;@/
+***************
+*** 12937,12945 ****
+ character of text to the user's input will cause each of these instructions
+ to be exercised one more time.
+ @^inner loop@>
+
+ @<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);@/
+ s:=char_height(f)(hd);@+if s>h then h:=s;
+ s:=char_depth(f)(hd);@+if s>d then d:=s;
+--- 12978,12998 ----
+ character of text to the user's input will cause each of these instructions
+ to be exercised one more time.
+ @^inner loop@>
++ This is also the place where the proper font variants are selected.
++
++ @d very_condensed_font=0
++ @d condensed_font=1
++ @d normal_font=2
++ @d extended_font=3
++ @d very_extended_font=4
+
+ @<Incorporate character dimensions into the dimensions of the hbox...@>=
+! begin
+! if line_font_variant<normal_font then
+! font(p):=font_variants[font(p)][line_font_variant]
+! else if line_font_variant>normal_font then
+! font(p):=font_variants[font(p)][line_font_variant-1];
+! f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
+ x:=x+char_width(f)(i);@/
+ s:=char_height(f)(hd);@+if s>h then h:=s;
+ s:=char_depth(f)(hd);@+if s>d then d:=s;
+***************
+*** 13036,13049 ****
+ pack_begin_line:=0;
+
+ @ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
+! if output_active then print(") has occurred while \output is active")
+ else begin if pack_begin_line<>0 then
+! begin if pack_begin_line>0 then print(") in paragraph at lines ")
+! else print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ end
+! else print(") detected at line ");
+ print_int(line);
+ end;
+ print_ln;@/
+--- 13089,13109 ----
+ pack_begin_line:=0;
+
+ @ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
+! case line_font_variant of
+! very_condensed_font: print(") (very condensed)");
+! condensed_font: print(") (condensed)");
+! normal_font: print(") (normal)");
+! extended_font: print(") (extended)");
+! very_extended_font: print(") (very extended)");
+! endcases;
+! if output_active then print(" has occurred while \output is active")
+ else begin if pack_begin_line<>0 then
+! begin if pack_begin_line>0 then print(" in paragraph at lines ")
+! else print(" in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ end
+! else print(" detected at line ");
+ print_int(line);
+ end;
+ print_ln;@/
+***************
+*** 13058,13066 ****
+ else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
+ end;
+! if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
+ begin last_badness:=1000000;
+! set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
+ @<Report an overfull hbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+ end
+--- 13118,13127 ----
+ else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
+ end;
+! max_shrink:=xn_over_d(total_shrink[o],effective_glue_overshrink,1000);
+! if (max_shrink<-x)and(o=normal)and(list_ptr(r)<>null) then
+ begin last_badness:=1000000;
+! glue_set(r):=unfloat(effective_glue_overshrink/1000);
+ @<Report an overfull hbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+ end
+***************
+*** 13077,13091 ****
+ else o:=normal
+
+ @ @<Report an overfull hbox and |goto common_ending|, if...@>=
+! if (-x-total_shrink[normal]>hfuzz)or(hbadness<100) then
+! begin if (overfull_rule>0)and(-x-total_shrink[normal]>hfuzz) then
+ begin while link(q)<>null do q:=link(q);
+ link(q):=new_rule;
+ width(link(q)):=overfull_rule;
+ end;
+ print_ln; print_nl("Overfull \hbox (");
+ @.Overfull \\hbox...@>
+! print_scaled(-x-total_shrink[normal]); print("pt too wide");
+ goto common_ending;
+ end
+
+--- 13138,13152 ----
+ else o:=normal
+
+ @ @<Report an overfull hbox and |goto common_ending|, if...@>=
+! if (-x-max_shrink>hfuzz)or(hbadness<100) then
+! begin if (overfull_rule>0)and(-x-max_shrink>hfuzz) then
+ begin while link(q)<>null do q:=link(q);
+ link(q):=new_rule;
+ width(link(q)):=overfull_rule;
+ end;
+ print_ln; print_nl("Overfull \hbox (");
+ @.Overfull \\hbox...@>
+! print_scaled(-x-max_shrink); print("pt too wide");
+ goto common_ending;
+ end
+
+***************
+*** 16038,16056 ****
+ @ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+ each break that is {\sl feasible}, in the sense that there is a way to end
+ a line at the given place without requiring any line to stretch more than
+! a given tolerance. A break node is characterized by three things: the position
+ of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+ or |disc_node|); the ordinal number of the line that will follow this
+! breakpoint; and the fitness classification of the line that has just
+! ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
+!
+! @d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their
+ shrinkability}
+! @d loose_fit=1 {fitness classification for lines stretching 0.5 to 1.0 of their
+ stretchability}
+! @d very_loose_fit=0 {fitness classification for lines stretching more than
+ their stretchability}
+! @d decent_fit=2 {fitness classification for all other lines}
+
+ @ The algorithm essentially determines the best possible way to achieve
+ each feasible combination of position, line, and fitness. Thus, it answers
+--- 16099,16124 ----
+ @ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+ each break that is {\sl feasible}, in the sense that there is a way to end
+ a line at the given place without requiring any line to stretch more than
+! a given tolerance. A break node is characterized by four things: the position
+ of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+ or |disc_node|); the ordinal number of the line that will follow this
+! breakpoint, the fitness classification of the line that has just
+! ended, i.e., |very_tight_fit|, |tight_fit|, |decent_fit|,
+! |loose_fit|, |very_loose_fit|, or |extremely_loose_fit|, and the font variant
+! to be used, |very_condensed_font|, |condensed_font|, |normal_font|,
+! |extended_font| or |very_extended_font|.
+!
+! @d very_tight_fit=5 {fitness classification for lines shrinking more than their
+! shrinkability}
+! @d tight_fit=4 {fitness classification for lines shrinking 0.5 to 1.0 of their
+ shrinkability}
+! @d decent_fit=3 {fitness classification for all other lines}
+! @d loose_fit=2 {fitness classification for lines stretching 0.5 to 1.0 of their
+ stretchability}
+! @d very_loose_fit=1 {fitness classification for lines stretching 1.0 to 2.0 of
+ their stretchability}
+! @d extremely_loose_fit=0 {fitness classification for lines stretching more
+! than 2.0 of their stretchability}
+
+ @ The algorithm essentially determines the best possible way to achieve
+ each feasible combination of position, line, and fitness. Thus, it answers
+***************
+*** 16063,16069 ****
+
+ An ``active node'' and a ``passive node'' are created in |mem| for each
+ feasible breakpoint that needs to be considered. Active nodes are three
+! words long and passive nodes are two words long. We need active nodes only
+ for breakpoints near the place in the paragraph that is currently being
+ examined, so they are recycled within a comparatively short time after
+ they are created.
+--- 16131,16137 ----
+
+ An ``active node'' and a ``passive node'' are created in |mem| for each
+ feasible breakpoint that needs to be considered. Active nodes are three
+! words long and passive nodes are too. We need active nodes only
+ for breakpoints near the place in the paragraph that is currently being
+ examined, so they are recycled within a comparatively short time after
+ they are created.
+***************
+*** 16095,16101 ****
+ to each other.
+
+ @d active_node_size=3 {number of words in active nodes}
+! @d fitness==subtype {|very_loose_fit..tight_fit| on final line for this break}
+ @d break_node==rlink {pointer to the corresponding passive node}
+ @d line_number==llink {line that begins at this breakpoint}
+ @d total_demerits(#)==mem[#+2].int {the quantity that \TeX\ minimizes}
+--- 16163,16170 ----
+ to each other.
+
+ @d active_node_size=3 {number of words in active nodes}
+! @d fitness==subtype {|extremely_loose_fit..very_tight_fit| on final line for this
+! break}
+ @d break_node==rlink {pointer to the corresponding passive node}
+ @d line_number==llink {line that begins at this breakpoint}
+ @d total_demerits(#)==mem[#+2].int {the quantity that \TeX\ minimizes}
+***************
+*** 16107,16113 ****
+ type(last_active):=hyphenated; line_number(last_active):=max_halfword;
+ subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
+
+! @ The passive node for a given breakpoint contains only four fields:
+
+ \yskip\hang|link| points to the passive node created just before this one,
+ if any, otherwise it is |null|.
+--- 16176,16182 ----
+ type(last_active):=hyphenated; line_number(last_active):=max_halfword;
+ subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
+
+! @ The passive node for a given breakpoint contains only five fields:
+
+ \yskip\hang|link| points to the passive node created just before this one,
+ if any, otherwise it is |null|.
+***************
+*** 16122,16137 ****
+ one created during the current pass. (This field is used only when
+ printing out detailed statistics about the line-breaking calculations.)
+
+ \yskip\noindent
+ There is a global variable called |passive| that points to the most
+ recently created passive node. Another global variable, |printed_node|,
+ is used to help print out the paragraph when detailed information about
+ the line-breaking computation is being displayed.
+
+! @d passive_node_size=2 {number of words in passive nodes}
+ @d cur_break==rlink {in passive node, points to position of this breakpoint}
+ @d prev_break==llink {points to passive node that should precede this one}
+ @d serial==info {serial number for symbolic identification}
+
+ @<Glob...@>=
+ @!passive:pointer; {most recent node on passive list}
+--- 16191,16211 ----
+ one created during the current pass. (This field is used only when
+ printing out detailed statistics about the line-breaking calculations.)
+
++ \yskip\hang|condensedness| is the font variant to be used. We store this
++ information in passive nodes rather than active ones, since it has to
++ stay around till the packaging, when all active nodes are long gone.
++
+ \yskip\noindent
+ There is a global variable called |passive| that points to the most
+ recently created passive node. Another global variable, |printed_node|,
+ is used to help print out the paragraph when detailed information about
+ the line-breaking computation is being displayed.
+
+! @d passive_node_size=3 {number of words in passive nodes}
+ @d cur_break==rlink {in passive node, points to position of this breakpoint}
+ @d prev_break==llink {points to passive node that should precede this one}
+ @d serial==info {serial number for symbolic identification}
++ @d condensedness(#)==mem[#+2].int
+
+ @<Glob...@>=
+ @!passive:pointer; {most recent node on passive list}
+***************
+*** 16153,16183 ****
+ pt, fil, fill, and filll appear in |mem[q+2..q+5].sc|; and the shrink difference
+ appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
+
+! @d delta_node_size=7 {number of words in a delta node}
+ @d delta_node=2 {|type| field in a delta node}
+
+! @ As the algorithm runs, it maintains a set of six delta-like registers
+ for the length of the line following the first active breakpoint to the
+ current position in the given hlist. When it makes a pass through the
+! active list, it also maintains a similar set of six registers for the
+ length following the active breakpoint of current interest. A third set
+ holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+ \.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+ When we pass a delta node we want to do operations like
+ $$\hbox{\ignorespaces|for
+! k:=1 to 6 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
+! want to do this without the overhead of |for| loops. The |do_all_six|
+! macro makes such six-tuples convenient.
+
+ @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}
+
+ @ Let's state the principles of the delta nodes more precisely and concisely,
+ so that the following programs will be less obscure. For each legal
+--- 16227,16261 ----
+ pt, fil, fill, and filll appear in |mem[q+2..q+5].sc|; and the shrink difference
+ appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
+
+! Delta nodes have four additional fields keeping track of the
+! difference in natural width for the font variants.
+!
+! @d delta_node_size=11 {number of words in a delta node}
+ @d delta_node=2 {|type| field in a delta node}
+
+! @ As the algorithm runs, it maintains a set of ten delta-like registers
+ for the length of the line following the first active breakpoint to the
+ current position in the given hlist. When it makes a pass through the
+! active list, it also maintains a similar set of ten registers for the
+ length following the active breakpoint of current interest. A third set
+ holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+ \.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+ When we pass a delta node we want to do operations like
+ $$\hbox{\ignorespaces|for
+! k:=1 to 10 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
+! want to do this without the overhead of |for| loops. The |do_all_ten|
+! macro makes such ten-tuples convenient. |do_all_six| is also needed.
+
+ @d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)
++ @d do_all_ten(#)==#(1);#(2);#(3);#(4);#(5);#(6);#(7);#(8);#(9);#(10)
+
+ @<Glo...@>=
+! @!active_width:array[1..10] of scaled;
+ {distance from first active node to~|cur_p|}
+! @!cur_active_width:array[1..10] of scaled; {distance from current active node}
+! @!background:array[1..10] of scaled; {length of an ``empty'' line}
+! @!break_width:array[1..10] of scaled; {length being computed after current break}
+
+ @ Let's state the principles of the delta nodes more precisely and concisely,
+ so that the following programs will be less obscure. For each legal
+***************
+*** 16258,16263 ****
+--- 16336,16345 ----
+ background[2+stretch_order(q)]:=stretch(q);@/
+ background[2+stretch_order(r)]:=@|background[2+stretch_order(r)]+stretch(r);@/
+ background[6]:=shrink(q)+shrink(r);
++ background[7]:=0;
++ background[8]:=0;
++ background[9]:=0;
++ background[10]:=0;
+
+ @ A pointer variable |cur_p| runs through the given horizontal list as we look
+ for breakpoints. This variable is global, since it is used both by |line_break|
+***************
+*** 16316,16322 ****
+ @<Other local variables for |try_break|@>@;
+ begin @<Make sure that |pi| is in the proper range@>;
+ no_break_yet:=true; prev_r:=active; old_l:=0;
+! do_all_six(copy_to_cur_active);
+ loop@+ begin continue: r:=link(prev_r);
+ @<If node |r| is of type |delta_node|, update |cur_active_width|,
+ set |prev_r| and |prev_prev_r|, then |goto continue|@>;
+--- 16398,16404 ----
+ @<Other local variables for |try_break|@>@;
+ begin @<Make sure that |pi| is in the proper range@>;
+ no_break_yet:=true; prev_r:=active; old_l:=0;
+! do_all_ten(copy_to_cur_active);
+ loop@+ begin continue: r:=link(prev_r);
+ @<If node |r| is of type |delta_node|, update |cur_active_width|,
+ set |prev_r| and |prev_prev_r|, then |goto continue|@>;
+***************
+*** 16341,16348 ****
+ @!f:internal_font_number; {used in character width calculation}
+ @!l:halfword; {line number of current active node}
+ @!node_r_stays_active:boolean; {should node |r| remain in the active list?}
+ @!line_width:scaled; {the current line will be justified to this width}
+! @!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line}
+ @!b:halfword; {badness of test line}
+ @!d:integer; {demerits of test line}
+ @!artificial_demerits:boolean; {has |d| been forced to zero?}
+--- 16423,16435 ----
+ @!f:internal_font_number; {used in character width calculation}
+ @!l:halfword; {line number of current active node}
+ @!node_r_stays_active:boolean; {should node |r| remain in the active list?}
++ @!do_not_consider:boolean;
+ @!line_width:scaled; {the current line will be justified to this width}
+! @!fit_class:extremely_loose_fit..very_tight_fit; {possible fitness class of
+! test line}
+! @!condensed_class,prev_cc:very_condensed_font..very_extended_font;
+! @!cw:scaled;
+! @!j:small_number;
+ @!b:halfword; {badness of test line}
+ @!d:integer; {demerits of test line}
+ @!artificial_demerits:boolean; {has |d| been forced to zero?}
+***************
+*** 16362,16368 ****
+ @<If node |r|...@>=
+ @^inner loop@>
+ if type(r)=delta_node then
+! begin do_all_six(update_width);
+ prev_prev_r:=prev_r; prev_r:=r; goto continue;
+ end
+
+--- 16449,16455 ----
+ @<If node |r|...@>=
+ @^inner loop@>
+ if type(r)=delta_node then
+! begin do_all_ten(update_width);
+ prev_prev_r:=prev_r; prev_r:=r; goto continue;
+ end
+
+***************
+*** 16381,16401 ****
+ @d awful_bad==@'7777777777 {more than a billion demerits}
+
+ @<Global...@>=
+! @!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
+! demerits known for current line class and position, given the fitness}
+ @!minimum_demerits:integer; {best total demerits known for current line class
+ and position}
+! @!best_place:array[very_loose_fit..tight_fit] of pointer; {how to achieve
+ |minimal_demerits|}
+! @!best_pl_line:array[very_loose_fit..tight_fit] of halfword; {corresponding
+ line number}
+
+ @ @<Get ready to start...@>=
+ minimum_demerits:=awful_bad;
+! minimal_demerits[tight_fit]:=awful_bad;
+! minimal_demerits[decent_fit]:=awful_bad;
+! minimal_demerits[loose_fit]:=awful_bad;
+! minimal_demerits[very_loose_fit]:=awful_bad;
+
+ @ The first part of the following code is part of \TeX's inner loop, so
+ we don't want to waste any time. The current active node, namely node |r|,
+--- 16468,16490 ----
+ @d awful_bad==@'7777777777 {more than a billion demerits}
+
+ @<Global...@>=
+! @!minimal_demerits:array[extremely_loose_fit..very_tight_fit,
+! very_condensed_font..very_extended_font] of integer; {best total
+! demerits known for current line class and position, given the fitness and condensedness}
+ @!minimum_demerits:integer; {best total demerits known for current line class
+ and position}
+! @!best_place:array[extremely_loose_fit..very_tight_fit,
+! very_condensed_font..very_extended_font] of pointer; {how to achieve
+ |minimal_demerits|}
+! @!best_pl_line:array[extremely_loose_fit..very_tight_fit,
+! very_condensed_font..very_extended_font] of halfword; {corresponding
+ line number}
+
+ @ @<Get ready to start...@>=
+ minimum_demerits:=awful_bad;
+! for l:=extremely_loose_fit to very_tight_fit do
+! for j:=very_condensed_font to very_extended_font do
+! minimal_demerits[l][j]:=awful_bad;
+
+ @ The first part of the following code is part of \TeX's inner loop, so
+ we don't want to waste any time. The current active node, namely node |r|,
+***************
+*** 16429,16439 ****
+ if abs(adj_demerits)>=awful_bad-minimum_demerits then
+ minimum_demerits:=awful_bad-1
+ else minimum_demerits:=minimum_demerits+abs(adj_demerits);
+! for fit_class:=very_loose_fit to tight_fit do
+! begin if minimal_demerits[fit_class]<=minimum_demerits then
+ @<Insert a new active node
+ from |best_place[fit_class]| to |cur_p|@>;
+! minimal_demerits[fit_class]:=awful_bad;
+ end;
+ minimum_demerits:=awful_bad;
+ @<Insert a delta node to prepare for the next active node@>;
+--- 16518,16529 ----
+ if abs(adj_demerits)>=awful_bad-minimum_demerits then
+ minimum_demerits:=awful_bad-1
+ else minimum_demerits:=minimum_demerits+abs(adj_demerits);
+! for fit_class:=extremely_loose_fit to very_tight_fit do
+! for condensed_class:=very_condensed_font to very_extended_font do
+! begin if minimal_demerits[fit_class][condensed_class]<=minimum_demerits then
+ @<Insert a new active node
+ from |best_place[fit_class]| to |cur_p|@>;
+! minimal_demerits[fit_class][condensed_class]:=awful_bad;
+ end;
+ minimum_demerits:=awful_bad;
+ @<Insert a delta node to prepare for the next active node@>;
+***************
+*** 16461,16467 ****
+ @d set_break_width_to_background(#)==break_width[#]:=background[#]
+
+ @<Compute the values of |break...@>=
+! begin no_break_yet:=false; do_all_six(set_break_width_to_background);
+ s:=cur_p;
+ if break_type>unhyphenated then if cur_p<>null then
+ @<Compute the discretionary |break_width| values@>;
+--- 16551,16557 ----
+ @d set_break_width_to_background(#)==break_width[#]:=background[#]
+
+ @<Compute the values of |break...@>=
+! begin no_break_yet:=false; do_all_ten(set_break_width_to_background);
+ s:=cur_p;
+ if break_type>unhyphenated then if cur_p<>null then
+ @<Compute the discretionary |break_width| values@>;
+***************
+*** 16524,16536 ****
+ @<Subtract the width of node |v|...@>=
+ if is_char_node(v) then
+ begin f:=font(v);
+! break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
+! end
+! else case type(v) of
+ ligature_node: begin f:=font(lig_char(v));@/
+! break_width[1]:=@|break_width[1]-
+! char_width(f)(char_info(f)(character(lig_char(v))));
+! end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(v);
+ othercases confusion("disc1")
+--- 16614,16638 ----
+ @<Subtract the width of node |v|...@>=
+ if is_char_node(v) then
+ begin f:=font(v);
+! cw:=char_width(f)(char_info(f)(character(v)));
+! break_width[1]:=break_width[1]-cw;
+! for j:=0 to 3 do
+! break_width[7+j]:=break_width[7+j]
+! -(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(v)))
+! -cw);
+! end else case type(v) of
+ ligature_node: begin f:=font(lig_char(v));@/
+! cw:=char_width(f)(char_info(f)(character(lig_char(v))));
+! break_width[1]:=@|break_width[1]-cw;
+! for j:=0 to 3 do
+! break_width[7+j]:=break_width[7+j]
+! -(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(lig_char(v))))
+! -cw);
+! end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(v);
+ othercases confusion("disc1")
+***************
+*** 16540,16552 ****
+ @ @<Add the width of node |s| to |b...@>=
+ if is_char_node(s) then
+ begin f:=font(s);
+! break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s)));
+! end
+! else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+! break_width[1]:=break_width[1]+
+! char_width(f)(char_info(f)(character(lig_char(s))));
+! end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+--- 16642,16666 ----
+ @ @<Add the width of node |s| to |b...@>=
+ if is_char_node(s) then
+ begin f:=font(s);
+! cw:=char_width(f)(char_info(f)(character(s)));
+! break_width[1]:=@|break_width[1]+cw;
+! for j:=0 to 3 do
+! break_width[7+j]:=break_width[7+j]
+! +(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(s)))
+! -cw);
+! end else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+! cw:=char_width(f)(char_info(f)(character(lig_char(s))));
+! break_width[1]:=break_width[1]+cw;
+! for j:=0 to 3 do
+! break_width[7+j]:=break_width[7+j]
+! +(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(lig_char(s))))
+! -cw);
+! end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+***************
+*** 16564,16577 ****
+
+ @<Insert a delta node to prepare for breaks at |cur_p|@>=
+ if type(prev_r)=delta_node then {modify an existing delta node}
+! begin do_all_six(convert_to_break_width);
+ end
+ else if prev_r=active then {no delta node needed at the beginning}
+! begin do_all_six(store_break_width);
+ end
+ else begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+! do_all_six(new_delta_to_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+--- 16678,16691 ----
+
+ @<Insert a delta node to prepare for breaks at |cur_p|@>=
+ if type(prev_r)=delta_node then {modify an existing delta node}
+! begin do_all_ten(convert_to_break_width);
+ end
+ else if prev_r=active then {no delta node needed at the beginning}
+! begin do_all_ten(store_break_width);
+ end
+ else begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+! do_all_ten(new_delta_to_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+***************
+*** 16585,16591 ****
+ if r<>last_active then
+ begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+! do_all_six(new_delta_from_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+--- 16699,16705 ----
+ if r<>last_active then
+ begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+! do_all_ten(new_delta_from_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+***************
+*** 16596,16606 ****
+ begin q:=get_node(passive_node_size);
+ link(q):=passive; passive:=q; cur_break(q):=cur_p;
+ @!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
+! prev_break(q):=best_place[fit_class];@/
+ q:=get_node(active_node_size); break_node(q):=passive;
+! line_number(q):=best_pl_line[fit_class]+1;
+ fitness(q):=fit_class; type(q):=break_type;
+! total_demerits(q):=minimal_demerits[fit_class];
+ link(q):=r; link(prev_r):=q; prev_r:=q;
+ @!stat if tracing_paragraphs>0 then
+ @<Print a symbolic description of the new break node@>;
+--- 16710,16721 ----
+ begin q:=get_node(passive_node_size);
+ link(q):=passive; passive:=q; cur_break(q):=cur_p;
+ @!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
+! condensedness(q):=condensed_class;
+! prev_break(q):=best_place[fit_class][condensed_class];@/
+ q:=get_node(active_node_size); break_node(q):=passive;
+! line_number(q):=best_pl_line[fit_class][condensed_class]+1;
+ fitness(q):=fit_class; type(q):=break_type;
+! total_demerits(q):=minimal_demerits[fit_class][condensed_class];
+ link(q):=r; link(prev_r):=q; prev_r:=q;
+ @!stat if tracing_paragraphs>0 then
+ @<Print a symbolic description of the new break node@>;
+***************
+*** 16612,16617 ****
+--- 16727,16733 ----
+ @.\AT!\AT!@>
+ print(": line "); print_int(line_number(q)-1);
+ print_char("."); print_int(fit_class);
++ print_char("."); print_int(condensed_class);
+ if break_type=hyphenated then print_char("-");
+ print(" t="); print_int(total_demerits(q));
+ print(" -> @@@@");
+***************
+*** 16703,16727 ****
+ We also deactivate node~|r| when a break at~|cur_p| is forced, since future
+ breaks must go through a forced break.
+
+ @<Consider the demerits for a line from |r| to |cur_p|...@>=
+ begin artificial_demerits:=false;@/
+ @^inner loop@>
+! shortfall:=line_width-cur_active_width[1]; {we're this much too short}
+! if shortfall>0 then
+! @<Set the value of |b| to the badness for stretching the line,
+! and compute the corresponding |fit_class|@>
+! else @<Set the value of |b| to the badness for shrinking the line,
+! and compute the corresponding |fit_class|@>;
+! if (b>inf_bad)or(pi=eject_penalty) then
+! @<Prepare to deactivate node~|r|, and |goto deactivate| unless
+! there is a reason to consider lines of text from |r| to |cur_p|@>
+! else begin prev_r:=r;
+! if b>threshold then goto continue;
+! node_r_stays_active:=true;
+! end;
+! @<Record a new feasible break@>;
+! if node_r_stays_active then goto continue; {|prev_r| has been set to |r|}
+! deactivate: @<Deactivate node |r|@>;
+ end
+
+ @ When a line must stretch, the available stretchability can be found in the
+--- 16819,16859 ----
+ We also deactivate node~|r| when a break at~|cur_p| is forced, since future
+ breaks must go through a forced break.
+
++ The program logic in this section is really f***ed up!!!
++
+ @<Consider the demerits for a line from |r| to |cur_p|...@>=
+ begin artificial_demerits:=false;@/
+ @^inner loop@>
+! node_r_stays_active:=false;
+! for condensed_class:=very_condensed_font to very_extended_font do begin
+! do_not_consider:=false;
+! shortfall:=line_width-cur_active_width[1];
+! case condensed_class of
+! very_condensed_font:shortfall:=shortfall-cur_active_width[7];
+! condensed_font: shortfall:=shortfall-cur_active_width[8];
+! extended_font: shortfall:=shortfall-cur_active_width[9];
+! very_extended_font: shortfall:=shortfall-cur_active_width[10];
+! endcases;
+! if shortfall>0 then
+! @<Set the value of |b| to the badness for stretching the line,
+! and compute the corresponding |fit_class|@>
+! else
+! @<Set the value of |b| to the badness for shrinking the line,
+! and compute the corresponding |fit_class|@>;
+! if (b>inf_bad)or(pi=eject_penalty) then begin
+! if final_pass and (minimum_demerits=awful_bad) and@|
+! (link(r)=last_active) and
+! (prev_r=active) then
+! artificial_demerits:=true {set demerits zero, this break is forced}
+! else if b>threshold then do_not_consider:=true;
+! end else begin
+! node_r_stays_active:=true;
+! if b>threshold then do_not_consider:=true;
+! end;
+! if not do_not_consider then @<Record a new feasible break@>
+! end;
+! if node_r_stays_active then prev_r:=r
+! else @<Deactivate node |r|@>;
+ end
+
+ @ When a line must stretch, the available stretchability can be found in the
+***************
+*** 16738,16789 ****
+ (cur_active_width[5]<>0) then
+ begin b:=0; fit_class:=decent_fit; {infinite stretch}
+ end
+! else begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
+! begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
+! end;
+ b:=badness(shortfall,cur_active_width[2]);
+ if b>12 then
+! if b>99 then fit_class:=very_loose_fit
+ else fit_class:=loose_fit
+ else fit_class:=decent_fit;
+- done1:
+ end
+
+ @ Shrinkability is never infinite in a paragraph;
+ we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
+
+ @<Set the value of |b| to the badness for shrinking...@>=
+! begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
+ else b:=badness(-shortfall,cur_active_width[6]);
+! if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
+! end
+!
+! @ During the final pass, we dare not lose all active nodes, lest we lose
+! touch with the line breaks already found. The code shown here makes sure
+! that such a catastrophe does not happen, by permitting overfull boxes as
+! a last resort. This particular part of \TeX\ was a source of several subtle
+! bugs before the correct program logic was finally discovered; readers
+! who seek to ``improve'' \TeX\ should therefore think thrice before daring
+! to make any changes here.
+! @^overfull boxes@>
+!
+! @<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
+! begin if final_pass and (minimum_demerits=awful_bad) and@|
+! (link(r)=last_active) and
+! (prev_r=active) then
+! artificial_demerits:=true {set demerits zero, this break is forced}
+! else if b>threshold then goto deactivate;
+! node_r_stays_active:=false;
+ end
+
+ @ When we get to this part of the code, the line from |r| to |cur_p| is
+ feasible, its badness is~|b|, and its fitness classification is |fit_class|.
+ We don't want to make an active node for this break yet, but we will
+ compute the total demerits and record them in the |minimal_demerits| array,
+ if such a break is the current champion among all ways to get to |cur_p|
+ in a given line-number class and fitness class.
+
+ @<Record a new feasible break@>=
+ if artificial_demerits then d:=0
+ else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
+ @!stat if tracing_paragraphs>0 then
+--- 16870,16906 ----
+ (cur_active_width[5]<>0) then
+ begin b:=0; fit_class:=decent_fit; {infinite stretch}
+ end
+! else begin
+ b:=badness(shortfall,cur_active_width[2]);
+ if b>12 then
+! if b>99 then
+! if b>799 then fit_class:=extremely_loose_fit
+! else fit_class:=very_loose_fit
+ else fit_class:=loose_fit
+ else fit_class:=decent_fit;
+ end
+
+ @ Shrinkability is never infinite in a paragraph;
+ we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
+
+ @<Set the value of |b| to the badness for shrinking...@>=
+! begin
+! if xn_over_d(-shortfall,1000,effective_glue_overshrink)>cur_active_width[6] then b:=inf_bad+1
+ else b:=badness(-shortfall,cur_active_width[6]);
+! if b>100 then fit_class:= very_tight_fit
+! else if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
+ end
+
+ @ When we get to this part of the code, the line from |r| to |cur_p| is
+ feasible, its badness is~|b|, and its fitness classification is |fit_class|.
++ And its condensedness classification is |condensed_class|.
+ We don't want to make an active node for this break yet, but we will
+ compute the total demerits and record them in the |minimal_demerits| array,
+ if such a break is the current champion among all ways to get to |cur_p|
+ in a given line-number class and fitness class.
+
+ @<Record a new feasible break@>=
++ begin
+ if artificial_demerits then d:=0
+ else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
+ @!stat if tracing_paragraphs>0 then
+***************
+*** 16791,16801 ****
+ tats@;@/
+ d:=d+total_demerits(r); {this is the minimum total demerits
+ from the beginning to |cur_p| via |r|}
+! if d<=minimal_demerits[fit_class] then
+! begin minimal_demerits[fit_class]:=d;
+! best_place[fit_class]:=break_node(r); best_pl_line[fit_class]:=l;
+ if d<minimum_demerits then minimum_demerits:=d;
+ end
+
+ @ @<Print a symbolic description of this feasible break@>=
+ begin if printed_node<>cur_p then
+--- 16908,16920 ----
+ tats@;@/
+ d:=d+total_demerits(r); {this is the minimum total demerits
+ from the beginning to |cur_p| via |r|}
+! if d<=minimal_demerits[fit_class][condensed_class] then
+! begin minimal_demerits[fit_class][condensed_class]:=d;
+! best_place[fit_class][condensed_class]:=break_node(r);
+! best_pl_line[fit_class][condensed_class]:=l;
+ if d<minimum_demerits then minimum_demerits:=d;
+ end
++ end
+
+ @ @<Print a symbolic description of this feasible break@>=
+ begin if printed_node<>cur_p then
+***************
+*** 16818,16823 ****
+--- 16937,16945 ----
+ @.*\relax@>
+ print(" p="); print_int(pi); print(" d=");
+ if artificial_demerits then print_char("*")@+else print_int(d);
++ print(" "); print_int(fit_class);
++ print("."); print_int(condensed_class);
++ print(" t="); print_int(d+total_demerits(r));
+ end
+
+ @ @<Print the list between |printed_node| and |cur_p|...@>=
+***************
+*** 16852,16858 ****
+ if (break_type=hyphenated)and(type(r)=hyphenated) then
+ if cur_p<>null then d:=d+double_hyphen_demerits
+ else d:=d+final_hyphen_demerits;
+! if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
+ end
+
+ @ When an active node disappears, we must delete an adjacent delta node if the
+--- 16974,16991 ----
+ if (break_type=hyphenated)and(type(r)=hyphenated) then
+ if cur_p<>null then d:=d+double_hyphen_demerits
+ else d:=d+final_hyphen_demerits;
+! case abs(fit_class-fitness(r)) of
+! 0,1: do_nothing;
+! 2: d:=d+adj_demerits;
+! othercases d:=d+adj_demerits+extra_adj_demerits;
+! endcases;
+! if break_node(r)=null then prev_cc:=normal_font
+! else prev_cc:=condensedness(break_node(r));
+! case abs(condensed_class-prev_cc) of
+! 0,1: do_nothing;
+! 2: d:=d+font_demerits;
+! othercases d:=d+font_demerits+extra_font_demerits;
+! endcases;
+ end
+
+ @ When an active node disappears, we must delete an adjacent delta node if the
+***************
+*** 16866,16887 ****
+ mem[prev_r+#].sc
+
+ @<Deactivate node |r|@>=
+ link(prev_r):=link(r); free_node(r,active_node_size);
+ if prev_r=active then @<Update the active widths, since the first active
+ node has been deleted@>
+ else if type(prev_r)=delta_node then
+ begin r:=link(prev_r);
+ if r=last_active then
+! begin do_all_six(downdate_width);
+ link(prev_prev_r):=last_active;
+ free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
+ end
+ else if type(r)=delta_node then
+! begin do_all_six(update_width);
+! do_all_six(combine_two_deltas);
+ link(prev_r):=link(r); free_node(r,delta_node_size);
+ end;
+ end
+
+ @ The following code uses the fact that |type(last_active)<>delta_node|. If the
+ active list has just become empty, we do not need to update the
+--- 16999,17022 ----
+ mem[prev_r+#].sc
+
+ @<Deactivate node |r|@>=
++ begin
+ link(prev_r):=link(r); free_node(r,active_node_size);
+ if prev_r=active then @<Update the active widths, since the first active
+ node has been deleted@>
+ else if type(prev_r)=delta_node then
+ begin r:=link(prev_r);
+ if r=last_active then
+! begin do_all_ten(downdate_width);
+ link(prev_prev_r):=last_active;
+ free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
+ end
+ else if type(r)=delta_node then
+! begin do_all_ten(update_width);
+! do_all_ten(combine_two_deltas);
+ link(prev_r):=link(r); free_node(r,delta_node_size);
+ end;
+ end
++ end
+
+ @ The following code uses the fact that |type(last_active)<>delta_node|. If the
+ active list has just become empty, we do not need to update the
+***************
+*** 16893,16900 ****
+ @<Update the active widths,...@>=
+ begin r:=link(active);
+ if type(r)=delta_node then
+! begin do_all_six(update_active);
+! do_all_six(copy_to_cur_active);
+ link(active):=link(r); free_node(r,delta_node_size);
+ end;
+ end
+--- 17028,17035 ----
+ @<Update the active widths,...@>=
+ begin r:=link(active);
+ if type(r)=delta_node then
+! begin do_all_ten(update_active);
+! do_all_ten(copy_to_cur_active);
+ link(active):=link(r); free_node(r,delta_node_size);
+ end;
+ end
+***************
+*** 16921,16929 ****
+--- 17056,17066 ----
+
+ @<Local variables for line breaking@>=
+ @!auto_breaking:boolean; {is node |cur_p| outside a formula?}
++ @!cw:scaled;
+ @!prev_p:pointer; {helps to determine when glue nodes are breakpoints}
+ @!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
+ @!f:internal_font_number; {used when calculating character widths}
++ @!l:small_number;
+
+ @ The `\ignorespaces|loop|\unskip' in the following code is performed at most
+ thrice per call of |line_break|, since it is actually a pass over the
+***************
+*** 16978,16984 ****
+ type(q):=unhyphenated; fitness(q):=decent_fit;
+ link(q):=last_active; break_node(q):=null;
+ line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
+! do_all_six(store_background);@/
+ passive:=null; printed_node:=temp_head; pass_number:=0;
+ font_in_short_display:=null_font
+
+--- 17115,17121 ----
+ type(q):=unhyphenated; fitness(q):=decent_fit;
+ link(q):=last_active; break_node(q):=null;
+ line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
+! do_all_ten(store_background);@/
+ passive:=null; printed_node:=temp_head; pass_number:=0;
+ font_in_short_display:=null_font
+
+***************
+*** 17024,17031 ****
+ kern_node: if subtype(cur_p)=explicit then kern_break
+ else act_width:=act_width+width(cur_p);
+ ligature_node: begin f:=font(lig_char(cur_p));
+! act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
+! end;
+ disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
+ math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
+ end;
+--- 17161,17175 ----
+ kern_node: if subtype(cur_p)=explicit then kern_break
+ else act_width:=act_width+width(cur_p);
+ ligature_node: begin f:=font(lig_char(cur_p));
+! cw:=char_width(f)(char_info(f)(character(lig_char(cur_p))));
+! act_width:=act_width+cw;
+! for j:=0 to 3 do
+! active_width[7+j]:=active_width[7+j]
+! +(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(lig_char(cur_p))))
+! -cw);
+! end;
+ disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
+ math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
+ end;
+***************
+*** 17047,17053 ****
+ @<Advance \(c)|cur_p| to the node following the present string...@>=
+ 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
+--- 17191,17204 ----
+ @<Advance \(c)|cur_p| to the node following the present string...@>=
+ begin prev_p:=cur_p;
+ repeat f:=font(cur_p);
+! cw:=char_width(f)(char_info(f)(character(cur_p)));
+! act_width:=act_width+cw;
+! for j:=0 to 3 do
+! active_width[7+j]:=active_width[7+j]
+! +(char_width(font_variants[f][j])
+! (char_info(font_variants[f][j])
+! (character(cur_p)))
+! -cw);
+ cur_p:=link(cur_p);
+ until not is_char_node(cur_p);
+ end
+***************
+*** 17378,17384 ****
+ else begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
+ cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
+ end;
+! adjust_tail:=adjust_head; just_box:=hpack(q,cur_width,exactly);
+ shift_amount(just_box):=cur_indent
+
+ @ Penalties between the lines of a paragraph come from club and widow lines,
+--- 17529,17538 ----
+ else begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
+ cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
+ end;
+! adjust_tail:=adjust_head;
+! line_font_variant:=condensedness(cur_p);
+! just_box:=hpack(q,cur_width,exactly);
+! line_font_variant:=normal_font;
+ shift_amount(just_box):=cur_indent
+
+ @ Penalties between the lines of a paragraph come from club and widow lines,
+***************
+*** 23263,23271 ****
+ @ Here is where the information for a new font gets loaded.
+
+ @<Assignments@>=
+! def_font: new_font(a);
+!
+ @ @<Declare subprocedures for |prefixed_command|@>=
+ procedure new_font(@!a:small_number);
+ label common_ending;
+ var u:pointer; {user's font identifier}
+--- 23417,23441 ----
+ @ Here is where the information for a new font gets loaded.
+
+ @<Assignments@>=
+! def_font: if cur_chr=0 then new_font(a) else
+! set_font_variant(a);
+!
+ @ @<Declare subprocedures for |prefixed_command|@>=
++ procedure set_font_variant(@!a:small_number);
++ var
++ s:integer;
++ begin
++ scan_int; s:=cur_val;
++ if (s<0)or(s>3) then begin
++ print_err("Bad font variant number");
++ help2("A font variant number must be between 0 and 3.")@/
++ ("I changed this one to zero."); int_error(s); s:=0;
++ end;
++ scan_font_ident; f:=cur_val;
++ scan_optional_equals; scan_font_ident;
++ font_variants[f][s]:=cur_val;
++ end;
++
+ procedure new_font(@!a:small_number);
+ label common_ending;
+ var u:pointer; {user's font identifier}
+***************
+*** 23274,23279 ****
+--- 23444,23450 ----
+ @!t:str_number; {name for the frozen font identifier}
+ @!old_setting:0..max_selector; {holds |selector| setting}
+ @!flushable_string:str_number; {string not yet referenced}
++ @!k:small_number;
+ begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+ @.texput@>
+***************
+*** 23292,23297 ****
+--- 23463,23469 ----
+ font number and |goto common_ending|@>;
+ f:=read_font_info(u,cur_name,cur_area,s);
+ common_ending: equiv(u):=f; eqtb[font_id_base+f]:=eqtb[u]; font_id_text(f):=t;
++ for k:=0 to 3 do font_variants[f][k]:=f;
+ end;
+
+ @ @<Scan the font size specification@>=
+*** tex.ch.orig Wed Sep 16 22:54:44 1998
+--- tex.ch Sun Sep 20 10:03:52 1998
+***************
+*** 1200,1211 ****
+ @z
+
+ @x [17.236] l.4954 - MLTeX: \charsubdefmax and \tracingcharsubdef
+! @d int_pars=55 {total number of integer parameters}
+ @y
+! @d char_sub_def_min_code=55 {smallest value in the charsubdef list}
+! @d char_sub_def_max_code=56 {largest value in the charsubdef list}
+! @d tracing_char_sub_def_code=57 {traces changes to a charsubdef def}
+! @d int_pars=58 {total number of integer parameters}
+ @z
+
+ @x [17.236] l.5016 - MLTeX: \charsubdefmax and \tracingcharsubdef
+--- 1200,1211 ----
+ @z
+
+ @x [17.236] l.4954 - MLTeX: \charsubdefmax and \tracingcharsubdef
+! @d int_pars=59 {total number of integer parameters}
+ @y
+! @d char_sub_def_min_code=59 {smallest value in the charsubdef list}
+! @d char_sub_def_max_code=60 {largest value in the charsubdef list}
+! @d tracing_char_sub_def_code=61 {traces changes to a charsubdef def}
+! @d int_pars=62 {total number of integer parameters}
+ @z
+
+ @x [17.236] l.5016 - MLTeX: \charsubdefmax and \tracingcharsubdef
+***************
+*** 1752,1757 ****
+--- 1752,1759 ----
+ {right boundary character, |non_char| if there is none}
+ @!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 four_fonts;
++ {we use four variants per font}
+ @y
+ @!font_info: ^fmemory_word;
+ {the big collection of font data}
+***************
+*** 1783,1788 ****
+--- 1785,1791 ----
+ {right boundary character, |non_char| if there is none}
+ @!font_false_bchar: ^nine_bits;
+ {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
++ @!font_variants: ^four_fonts;
+ @z
+
+ @x [30.550] l.10723 - texarray
+***************
+*** 1845,1850 ****
+--- 1848,1854 ----
+ font_glue[null_font]:=null; font_params[null_font]:=7;
+ param_base[null_font]:=-1;
+ for k:=0 to 6 do font_info[k].sc:=0;
++ for k:=0 to 3 do font_variants[null_font][k]:=null_font;
+ @y
+ @z
+
+***************
+*** 2176,2184 ****
+ @z
+
+ @x [38.859] l.16855 - Fix a casting/expression evaluation problem.
+! if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
+ @y
+! if abs(toint(fit_class)-toint(fitness(r)))>1 then d:=d+adj_demerits;
+ @z
+
+ @x [39.875] l.17170 - Another casting problem.
+--- 2180,2188 ----
+ @z
+
+ @x [38.859] l.16855 - Fix a casting/expression evaluation problem.
+! case abs(fit_class-fitness(r)) of
+ @y
+! case abs(toint(fit_class)-toint(fitness(r))) of
+ @z
+
+ @x [39.875] l.17170 - Another casting problem.
+***************
+*** 3197,3202 ****
+--- 3201,3207 ----
+ dump_things(exten_base[null_font], font_ptr+1-null_font);
+ dump_things(param_base[null_font], font_ptr+1-null_font);
+ dump_things(font_glue[null_font], font_ptr+1-null_font);
++ dump_things(font_variants[null_font], font_ptr+1-null_font);
+ dump_things(bchar_label[null_font], font_ptr+1-null_font);
+ dump_things(font_bchar[null_font], font_ptr+1-null_font);
+ dump_things(font_false_bchar[null_font], font_ptr+1-null_font);
+***************
+*** 3264,3269 ****
+--- 3269,3275 ----
+ xmalloc_array(kern_base, font_max);
+ xmalloc_array(exten_base, font_max);
+ xmalloc_array(param_base, font_max);
++ xmalloc_array(font_variants, font_max);
+
+ undump_things(font_check[null_font], font_ptr+1-null_font);
+ undump_things(font_size[null_font], font_ptr+1-null_font);
+***************
+*** 3290,3295 ****
+--- 3296,3302 ----
+ undump_things(param_base[null_font], font_ptr+1-null_font);
+ undump_checked_things(min_halfword, lo_mem_max,
+ font_glue[null_font], font_ptr+1-null_font);
++ undump_things(font_variants[null_font], font_ptr+1-null_font);
+ undump_checked_things(0, fmem_ptr-1,
+ bchar_label[null_font], font_ptr+1-null_font);
+ undump_checked_things(min_quarterword, non_char,
+***************
+*** 3705,3710 ****
+--- 3712,3718 ----
+ xmalloc_array(kern_base, font_max);
+ xmalloc_array(exten_base, font_max);
+ xmalloc_array(param_base, font_max);
++ xmalloc_array(font_variants, font_max);
+
+ font_ptr:=null_font; fmem_ptr:=7;
+ font_name[null_font]:="nullfont"; font_area[null_font]:="";
+***************
+*** 3720,3725 ****
+--- 3728,3734 ----
+ font_glue[null_font]:=null; font_params[null_font]:=7;
+ param_base[null_font]:=-1;
+ for font_k:=0 to 6 do font_info[font_k].sc:=0;
++ for font_k:=0 to 3 do font_variants[null_font][font_k]:=null_font;
+ end;
+ tini@/
+