@x {margin int_pars go here} @y @d default_margin_char_code=81 @z @x @d error_context_lines==int_par(error_context_lines_code) @y @d error_context_lines==int_par(error_context_lines_code) @d default_margin_char==int_par(default_margin_char_code) @z @x error_context_lines_code:print_esc("errorcontextlines"); @y error_context_lines_code:print_esc("errorcontextlines"); default_margin_char_code:print_esc("defaultmarginchar"); @z @x primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ @!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> @y primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ @!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> primitive("defaultmarginchar",assign_int,int_base+default_margin_char_code);@/ @!@:default_margin_char_}{\.{\\defaultmarginchar} primitive@> @z @x @!font_false_bchar:array[internal_font_number] of min_quarterword..non_char; {|font_bchar| if it doesn't exist in the font, otherwise |non_char|} @y @!font_false_bchar:array[internal_font_number] of min_quarterword..non_char; {|font_bchar| if it doesn't exist in the font, otherwise |non_char|} @!margin_char:array[internal_font_number] of integer; {current \.{\\marginchar} values} @z @x for k:=0 to 6 do font_info[k].sc:=0; @y for k:=0 to 6 do font_info[k].sc:=0; margin_char[null_font]:=-1; @z @x hyphen_char[f]:=default_hyphen_char; skew_char[f]:=default_skew_char; @y hyphen_char[f]:=default_hyphen_char; skew_char[f]:=default_skew_char; margin_char[f]:=default_margin_char; @z @x @d copy_to_cur_active(#)==cur_active_width[#]:=active_width[#] @d deactivate=60 {go here when node |r| should be deactivated} @= @y The function |kerning| returns the kerning between the characters |cl| and |cr| in font |f|, or zero if there is none. @d copy_to_cur_active(#)==cur_active_width[#]:=active_width[#] @d deactivate=60 {go here when node |r| should be deactivated} @= function kerning(f,cl,cr:eight_bits):scaled; label done; var s:scaled; i:four_quarters; a:integer; begin s:=0; i:=char_info(f)(cl); if char_tag(i)=lig_tag then begin a:=lig_kern_start(f)(i); i:=font_info[a].qqqq; if skip_byte(i)>stop_flag then begin a:=lig_kern_restart(f)(i); i:=font_info[a].qqqq; end; loop@+ begin if next_char(i)=cr then begin if op_byte(i)>=kern_flag then if skip_byte(i)<=stop_flag then s:=char_kern(f)(i); goto done; end; if skip_byte(i)>=stop_flag then goto done; a:=a+qo(skip_byte(i))+1; i:=font_info[a].qqqq; end; end; done: kerning:=s; end; @ The function |margin_correction| expects an active node |r| and a |cur_p| pointing directly into the horizontal list with the tentative line to be broken extending from |r| to |cur_p|. It returns the sum of the margin corrections for the line between them. It does so by finding the first node in the line, skipping everything that would be dropped after a break (including a possible |\parindent| box), and calculating the kern between the |marginchar| and it. Similarly, the last node of the line is used to find the kern between it and the |marginchar|. This routine could be made more efficient by avoiding the list traversal. The main loop of |try_break| could remember the last node before |cur_p|. @= function margin_correction(r,cur_p:pointer):scaled; label done; var left_kern,right_kern:scaled; sl,s:pointer; f,c:eight_bits; ii:four_quarters; rc,a:integer; begin left_kern:=0; right_kern:=0; if break_node(r)=null then begin sl:=temp_head; if (type(link(sl))=hlist_node)and (list_ptr(link(sl))=null) then begin sl:=link(sl); { skip |\parindent| box } end; end else sl:=cur_break(break_node(r)); s:=sl; if type(s)=disc_node then begin if post_break(s)<>null then s:=post_break(s) else begin rc:=replace_count(s); s:=link(s); while rc>0 do begin if link(s)<>null then s:=link(s); decr(rc); end; end; end; f:=null_font; c:=qi(0); while s<>null do begin if is_char_node(s) then begin f:=font(s); c:=character(s); s:=null; end else case type(s) of glue_node: s:=link(s); penalty_node: s:=link(s); ligature_node: begin f:=font(lig_char(s)); c:=character(lig_char(s)); s:=null; end; math_node: s:=link(s); kern_node: if subtype(s)<>explicit then s:=null else s:=link(s); othercases s:=null; endcases;@/ end; { if we reach this point, |(f,c)| is the font-char pair starting the line } if (0<=margin_char[f])and(margin_char[f]<=255) then begin left_kern:=kerning(f,qi(margin_char[f]),c); end; s:=null; if cur_p then if type(cur_p)=disc_node then begin if pre_break(cur_p)<>null then begin s:=pre_break(cur_p); while link(s)<>null do s:=link(s); { unnecessary list traversal! } goto done; end; end; s:=sl; while link(s)<>cur_p do begin s:=link(s); if s=null then goto done; end; done: f:=null_font; c:=qi(0); if s<>null then if is_char_node(s) then begin f:=font(s); c:=character(s); end else if type(s)=ligature_node then begin f:=font(lig_char(s)); c:=character(lig_char(s)); end; { if we reach this point, |(f,c)| is the font-char pair ending the line } if (0<=margin_char[f])and(margin_char[f]<=255) then begin right_kern:=kerning(f,c,qi(margin_char[f])); end; margin_correction:=left_kern+right_kern; end; @z @x @!line_width:scaled; {the current line will be justified to this width} @y @!line_width:scaled; {the current line will be justified to this width} @!local_line_width:scaled; {|line_width| including |marginchar| kerning} @z @x if hz_en then begin if cur_active_width[1]+cur_active_width[7]line_width then shortfall:=line_width-(cur_active_width[1]-cur_active_width[8]) else shortfall:=0; end else shortfall:=line_width-cur_active_width[1]; {we're this much too short} @y local_line_width:=line_width-margin_correction(r,cur_p); if hz_en then begin if cur_active_width[1]+cur_active_width[7]local_line_width then shortfall:=line_width-(cur_active_width[1]-cur_active_width[8]) else shortfall:=0; end else shortfall:=local_line_width-cur_active_width[1]; {we're this much too short} @z @x procedure post_line_break(@!final_widow_penalty:integer); label done,done1; var q,@!r,@!s:pointer; {temporary registers for list manipulation} @y procedure post_line_break(@!final_widow_penalty:integer); label done,done1,done2,done3; var q,@!r,@!s:pointer; {temporary registers for list manipulation} f,c:eight_bits; ss:scaled; @z @x @; @; @y @; @; @; @z @x @= r:=link(q); link(q):=null; q:=link(temp_head); link(temp_head):=r; if left_skip<>zero_glue then begin r:=new_param_glue(left_skip_code); link(r):=q; q:=r; end @y We change this code to always insert the \.{\\leftskip}. @= r:=link(q); link(q):=null; q:=link(temp_head); link(temp_head):=r; r:=new_param_glue(left_skip_code); link(r):=q; q:=r @ When we reach this code, |q| points to the line in question. The line starts with \.{\\leftskip} glue and ends with \.{\\rightskip} glue. If the first node after the |leftskip| is a character~|c| whose font has a |marginchar|~|m|, we insert the kerning |m|--|c| between the |leftskip| node and charnode~|c|. If the last node before the |rightskip is a character~|c'| whose font has a |marginchar|~|m'|, we insert the kerning |c'|--|m'| between charnode~|c'| and the |rightskip| node. @= s:=q; r:=link(s); if cur_line=prev_graf+1 then begin {treat the first line specially} if (type(r)=hlist_node)and(list_ptr(r)=null) then begin {skip |parindent| box} s:=r; r:=link(s); end; end; ss:=0; if is_char_node(r) then begin f:=font(r); c:=character(r); end else if type(r)=ligature_node then begin f:=font(lig_char(r)); c:=character(lig_char(r)); end else goto done2; if (0<=margin_char[f])and(margin_char[f]<=255) then begin ss:=kerning(f,qi(margin_char[f]),c); end; done2: if ss<>0 then begin s:=new_kern(ss); link(q):=s; link(s):=r; s:=link(q); end; while link(r)<>null do begin s:=r; r:=link(r); { unnecessary list traversal! } end; ss:=0; if is_char_node(s) then begin f:=font(s); c:=character(s); end else if type(s)=ligature_node then begin f:=font(lig_char(s)); c:=character(lig_char(s)); end else goto done3; if (0<=margin_char[f])and(margin_char[f]<=255) then begin ss:=kerning(f,c,qi(margin_char[f])); end; done3: if ss<>0 then begin link(s):=new_kern(ss); link(link(s)):=r; end; @z @x assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; scan_optional_equals; scan_int; if n=0 then hyphen_char[f]:=cur_val@+else skew_char[f]:=cur_val; end; @y assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val; scan_optional_equals; scan_int; case n of 0: hyphen_char[f]:=cur_val; 1: skew_char[f]:=cur_val; othercases margin_char[f]:=cur_val; endcases; end; @z @x primitive("skewchar",assign_font_int,1); @!@:skew_char_}{\.{\\skewchar} primitive@> @y primitive("skewchar",assign_font_int,1); @!@:skew_char_}{\.{\\skewchar} primitive@> primitive("marginchar",assign_font_int,2); @!@:margin_char_}{\.{\\marginchar} primitive@> @z @x assign_font_int: if chr_code=0 then print_esc("hyphenchar") else print_esc("skewchar"); @y assign_font_int: case chr_code of 0: print_esc("hyphenchar"); 1: print_esc("skewchar"); othercases print_esc("marginchar"); endcases; @z