*** 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) + @= 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@> @ @= assign_int: if chr_code 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 ---- @= @!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; @ @= primitive("nullfont",set_font,null_font); *************** *** 12854,12865 **** @< Glob...@>= @!adjust_tail:pointer; {tail of adjustment list} @ @=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|} @ @=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@> @= ! 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 @= ! begin ! if line_font_variantnormal_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; @ @= ! 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; @ @= ! 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} @; 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); @; end *************** *** 13077,13091 **** else o:=normal @ @= ! 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 @ @= ! 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} @= @!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 @= @!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) @= ! @!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) @= ! @!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 **** @@; begin @; no_break_yet:=true; prev_r:=active; old_l:=0; ! do_all_six(copy_to_cur_active); loop@+ begin continue: r:=link(prev_r); @; --- 16398,16404 ---- @@; begin @; no_break_yet:=true; prev_r:=active; old_l:=0; ! do_all_ten(copy_to_cur_active); loop@+ begin continue: r:=link(prev_r); @; *************** *** 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 **** @= @^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 ---- @= @^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} @= ! @!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} @ @= 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} @= ! @!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} @ @= 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 @; ! minimal_demerits[fit_class]:=awful_bad; end; minimum_demerits:=awful_bad; @; --- 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 @; ! minimal_demerits[fit_class][condensed_class]:=awful_bad; end; minimum_demerits:=awful_bad; @; *************** *** 16461,16467 **** @d set_break_width_to_background(#)==break_width[#]:=background[#] @= ! 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 @; --- 16551,16557 ---- @d set_break_width_to_background(#)==break_width[#]:=background[#] @= ! 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 @; *************** *** 16524,16536 **** @= 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 ---- @= 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 **** @ @= 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 ---- @ @= 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 **** @= 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 ---- @= 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 @; --- 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 @; *************** *** 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. @= begin artificial_demerits:=false;@/ @^inner loop@> ! shortfall:=line_width-cur_active_width[1]; {we're this much too short} ! if shortfall>0 then ! @ ! else @; ! if (b>inf_bad)or(pi=eject_penalty) then ! @ ! else begin prev_r:=r; ! if b>threshold then goto continue; ! node_r_stays_active:=true; ! end; ! @; ! if node_r_stays_active then goto continue; {|prev_r| has been set to |r|} ! deactivate: @; 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!!! + @= 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 ! @ ! else ! @; ! 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 @ ! end; ! if node_r_stays_active then prev_r:=r ! else @; 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]|. @= ! 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@> ! ! @= ! 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. @= if artificial_demerits then d:=0 else @; @!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]|. @= ! 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. @= + begin if artificial_demerits then d:=0 else @; @!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= 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= 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 @ @= *************** *** 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 @= link(prev_r):=link(r); free_node(r,active_node_size); if prev_r=active then @ 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 @= + begin link(prev_r):=link(r); free_node(r,active_node_size); if prev_r=active then @ 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 **** @= 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 ---- @= 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 ---- @= @!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: @; 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: @; math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break; end; *************** *** 17047,17053 **** @= 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 ---- @= 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. @= ! def_font: new_font(a); ! @ @= 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. @= ! def_font: if cur_chr=0 then new_font(a) else ! set_font_variant(a); ! @ @= + 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; @ @= *** 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@/