diff options
Diffstat (limited to 'systems/tex-extensions/clasen/hz')
l--------- | systems/tex-extensions/clasen/hz/etex.bib | 1 | ||||
-rw-r--r-- | systems/tex-extensions/clasen/hz/hz-glue.ch | 26 | ||||
-rw-r--r-- | systems/tex-extensions/clasen/hz/hz-post-web2c.ch | 76 | ||||
-rw-r--r-- | systems/tex-extensions/clasen/hz/hz.ch | 465 | ||||
-rw-r--r-- | systems/tex-extensions/clasen/hz/hz.tex | 104 |
5 files changed, 672 insertions, 0 deletions
diff --git a/systems/tex-extensions/clasen/hz/etex.bib b/systems/tex-extensions/clasen/hz/etex.bib new file mode 120000 index 0000000000..59519b0c14 --- /dev/null +++ b/systems/tex-extensions/clasen/hz/etex.bib @@ -0,0 +1 @@ +../etex.bib
\ No newline at end of file diff --git a/systems/tex-extensions/clasen/hz/hz-glue.ch b/systems/tex-extensions/clasen/hz/hz-glue.ch new file mode 100644 index 0000000000..e38a1f4572 --- /dev/null +++ b/systems/tex-extensions/clasen/hz/hz-glue.ch @@ -0,0 +1,26 @@ +@x +@p function adjust_excess(p:pointer;r:real):scaled; +@y +@p function adjust_excess(p:pointer;r:glue_ratio):scaled; +@z + +@x + d:=abs(v+char_width(f)(i)-r*w); +@y + d:=abs(v+char_width(f)(i)-glue_mult(r,w)); +@z + +@x + dd:=abs(v+char_width(ff)(ii)-r*w); +@y + dd:=abs(v+char_width(ff)(ii)-glue_mult(r,w)); +@z + +@x + x:=x-adjust_excess(link(q),(gw+x)/gw); +@y + { |adjust_excess| tries to make up the excess~|x| by stretching the glyphs + by the factor |gw+x/gw| (represented as a |glue_ratio| value). } + x:=x-adjust_excess(link(q),glue_fix(gw,gw+x,gw)); +@z + diff --git a/systems/tex-extensions/clasen/hz/hz-post-web2c.ch b/systems/tex-extensions/clasen/hz/hz-post-web2c.ch new file mode 100644 index 0000000000..f6c17e3af6 --- /dev/null +++ b/systems/tex-extensions/clasen/hz/hz-post-web2c.ch @@ -0,0 +1,76 @@ +% +% This is web2c-specific. +% + +@x +@!font_variants:array[internal_font_number] of min_halfword..max_halfword; +@y +@!font_variants:^pointer; +@z + +% +% web2c moves the initialization to somewhere else. +% + +@x +font_variants[null_font]:=null; +@y +@z + +% +% We must dump the font_variants table as well. +% + +@x +dump_things(font_glue[null_font], font_ptr+1-null_font); +@y +dump_things(font_glue[null_font], font_ptr+1-null_font); +dump_things(font_variants[null_font], font_ptr+1-null_font); +@z + +% +% This is web2c-specific. +% + +@x +xmalloc_array(param_base, font_max); +@y +xmalloc_array(param_base, font_max); +xmalloc_array(font_variants, font_max); +@z + +% +% We must undump the font_variants table as well. +% + +@x +undump_checked_things(min_halfword, lo_mem_max, + font_glue[null_font], font_ptr+1-null_font); +@y +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); +@z + +% +% This is web2c-specific. +% + +@x + xmalloc_array(param_base, font_max); +@y + xmalloc_array(param_base, font_max); + xmalloc_array(font_variants, font_max); +@z + +% +% web2c moves the initialization to somewhere else. +% + +@x + param_base[null_font]:=-1; +@y + param_base[null_font]:=-1; + font_variants[null_font]:=null; +@z + diff --git a/systems/tex-extensions/clasen/hz/hz.ch b/systems/tex-extensions/clasen/hz/hz.ch new file mode 100644 index 0000000000..b2b2d9f6f5 --- /dev/null +++ b/systems/tex-extensions/clasen/hz/hz.ch @@ -0,0 +1,465 @@ +@x +{hz int_pars go here} +@y +@d hz_state_code=80 +@z + +@x +@d error_context_lines==int_par(error_context_lines_code) +@y +@d hz_state==int_par(hz_state_code) +@d hz_en==(hz_state>0) +@d error_context_lines==int_par(error_context_lines_code) +@z + +@x +error_context_lines_code:print_esc("errorcontextlines"); +@y +error_context_lines_code:print_esc("errorcontextlines"); +hz_state_code:print_esc("hzstate"); +@z + +@x +primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ +@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +@y +primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/ +@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@> +primitive("hzstate",assign_int,int_base+hz_state_code);@/ +@!@:hz_state_}{\.{\\hzstate} primitive@> +@z + +@x +escape_char:="\"; end_line_char:=carriage_return; +@y +escape_char:="\"; end_line_char:=carriage_return; +hz_state:=0; +@z + +@x +primitive("font",def_font,0);@/ +@!@:font_}{\.{\\font} primitive@> +@y +primitive("font",def_font,0);@/ +@!@:font_}{\.{\\font} primitive@> +primitive("fontvariant",def_font,1);@/ +@!@:fontvariant_}{\.{\\fontvariant} primitive@> +@z + +@x +def_font: print_esc("font"); +@y +def_font: if chr_code=0 then print_esc("font") else print_esc("fontvariant"); +@z + +@x l.8326 +@p procedure scan_something_internal(@!level:small_number;@!negative:boolean); + {fetch an internal parameter} +var m:halfword; {|chr_code| part of the operand token} +@y +@p procedure scan_something_internal(@!level:small_number;@!negative:boolean); + {fetch an internal parameter} +var m:halfword; {|chr_code| part of the operand token} +r:pointer; {used with font variants} +@z + +@x l.8379 +else begin back_input; scan_font_ident; + scanned_result(font_id_base+cur_val)(ident_val); + end +@y +else begin + if m=0 then begin + back_input; scan_font_ident; + scanned_result(font_id_base+cur_val)(ident_val); + end else if hz_en then begin + scan_font_ident; + r:=font_variants[cur_val]; cur_val:=0; + while r<>null do begin + incr(cur_val); r:=link(r); + end; + scanned_result(cur_val)(int_val); + end else begin + print_err("Improper "); print_cmd_chr(def_font,m); + error; + end; +end +@z + +@x l.10710 +@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char; + {|font_bchar| if it doesn't exist in the font, otherwise |non_char|} +@y +@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char; + {|font_bchar| if it doesn't exist in the font, otherwise |non_char|} +@!font_variants:array[internal_font_number] of min_halfword..max_halfword; +@z + +@x l.10762 +for k:=0 to 6 do font_info[k].sc:=0; +@y +for k:=0 to 6 do font_info[k].sc:=0; +font_variants[null_font]:=null; +@z + +@x l.11189 +fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f; goto done +@y +fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f; +for a:=0 to 255 do begin + qw:=char_info(f)(a); + if char_exists(qw) then begin + font_variants[f]:=get_avail; + font(font_variants[f]):=f; + character(font_variants[f]):=a; + link(font_variants[f]):=null; + goto done; + end; +end; +goto done; +@z + +@x l.12860 +@ Here now is |hpack|, which contains few if any surprises. + +@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; +label reswitch, common_ending, exit; +var r:pointer; {the box node that will be returned} +@!q:pointer; {trails behind |p|} +@!h,@!d,@!x:scaled; {height, depth, and natural width} +@y +@ This routine replaces characters in the hlist |p| with variants from other +fonts in order to stretch the natural width by |r|. It returns the amount by +which the natural width could be stretched. + +@p function adjust_excess(p:pointer;r:real):scaled; +label reswitch,found,done; +var v,w,d,dd:scaled; +f,ff,c:eight_bits; +i,ii:four_quarters; +s:pointer; +j:integer; +begin +@!debug +print("(adjust_excess: "); print_scaled(round(unity*r)); +print(" --> "); +gubed +v:=0; w:=0; +while p<>null do begin + reswitch: while is_char_node(p) do begin + c:=character(p); f:=font(p); i:=char_info(f)(c); + w:=w+char_width(f)(i); + s:=font_variants[f]; + f:=font(s); i:=char_info(f)(c); + d:=abs(v+char_width(f)(i)-r*w); + s:=link(s); +@!debug + j:=0; +gubed + while s<>null do begin + ff:=font(s); ii:=char_info(ff)(c); + dd:=abs(v+char_width(ff)(ii)-r*w); + if dd>=d then goto found; + d:=dd; f:=ff; i:=ii; +@!debug + j:=j+1; +gubed + s:=link(s); + end; + found: + font(p):=f; +@!debug + print_int(j); +gubed + v:=v+char_width(f)(i); + p:=link(p); + end; + if p=null then goto done; + if type(p)=ligature_node then + @<Make node |p| look like a |char_node| and |goto reswitch|@> + else p:=link(p); +end; +done: +@!debug +print_ln; +print(" got "); print_scaled(v-w); print(")"); +gubed +adjust_excess:=v-w; +end; + +@ Here now is |hpack|, which contains few if any surprises. + +@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; +label reswitch, common_ending, exit; +var r:pointer; {the box node that will be returned} +@!q:pointer; {trails behind |p|} +@!h,@!d,@!x:scaled; {height, depth, and natural width} +@!gw:scaled; {natural width coming from glyphs} +@z + +@x +h:=0; @<Clear dimensions to zero@>; +@y +h:=0; gw:=0; @<Clear dimensions to zero@>; +@z + +@x +@<Incorporate character dimensions into the dimensions of the hbox...@>= +begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i); +x:=x+char_width(f)(i);@/ +@y +@<Incorporate character dimensions into the dimensions of the hbox...@>= +begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i); +x:=x+char_width(f)(i);@/ +gw:=gw+char_width(f)(i); +@z + +@x +else if x>0 then @<Determine horizontal glue stretch setting, then |return| + or \hbox{|goto common_ending|}@> +else @<Determine horizontal glue shrink setting, then |return| + or \hbox{|goto common_ending|}@> +@y +else begin + if hz_en then begin + if gw<>0 then begin + x:=x-adjust_excess(link(q),(gw+x)/gw); + end; + end; + if x>0 then @<Determine horizontal glue stretch setting, then |return| + or \hbox{|goto common_ending|}@> + else @<Determine horizontal glue shrink setting, then |return| + or \hbox{|goto common_ending|}@>; +end +@z + +@x +@d delta_node_size=7 {number of words in a delta node} +@d delta_node=2 {|type| field in a delta node} +@y +@d delta_node_size=9 {number of words in a delta node} +@d delta_node=2 {|type| field in a delta node} +@z + +@x +@d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6) + +@<Glo...@>= +@!active_width:array[1..6] of scaled; + {distance from first active node to~|cur_p|} +@!cur_active_width:array[1..6] of scaled; {distance from current active node} +@!background:array[1..6] of scaled; {length of an ``empty'' line} +@!break_width:array[1..6] of scaled; {length being computed after current break} +@y +For the hz algorithm, we add two more fields to store the finite +stretch and shrink from glyphs. + +@d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6) +@d do_all_eight(#)==do_all_six(#);#(7);#(8) + +@<Glo...@>= +@!active_width:array[1..8] of scaled; + {distance from first active node to~|cur_p|} +@!cur_active_width:array[1..8] of scaled; {distance from current active node} +@!background:array[1..8] of scaled; {length of an ``empty'' line} +@!break_width:array[1..8] of scaled; {length being computed after current break} +@z + +@x +background[6]:=shrink(q)+shrink(r); +@y +background[6]:=shrink(q)+shrink(r); +background[7]:=0; background[8]:=0; +@z + +@x +do_all_six(copy_to_cur_active); +@y +do_all_eight(copy_to_cur_active); +@z + +@x + begin do_all_six(update_width); +@y + begin do_all_eight(update_width); +@z + +@x +begin no_break_yet:=false; do_all_six(set_break_width_to_background); +@y +begin no_break_yet:=false; do_all_eight(set_break_width_to_background); +@z + +@x + begin do_all_six(convert_to_break_width); +@y + begin do_all_eight(convert_to_break_width); +@z + +@x + begin do_all_six(store_break_width); +@y + begin do_all_eight(store_break_width); +@z + +@x + do_all_six(new_delta_to_break_width); +@y + do_all_eight(new_delta_to_break_width); +@z + +@x + do_all_six(new_delta_from_break_width); +@y + do_all_eight(new_delta_from_break_width); +@z + +@x +shortfall:=line_width-cur_active_width[1]; {we're this much too short} +@y +if hz_en then begin + if cur_active_width[1]+cur_active_width[7]<line_width then + shortfall:=line_width-(cur_active_width[1]+cur_active_width[7]) + else if cur_active_width[1]-cur_active_width[8]>line_width then + shortfall:=line_width-(cur_active_width[1]-cur_active_width[8]) + else shortfall:=0; +end else shortfall:=line_width-cur_active_width[1]; {we're this much too short} +@z + +@x + begin do_all_six(downdate_width); +@y + begin do_all_eight(downdate_width); +@z + +@x + begin do_all_six(update_width); + do_all_six(combine_two_deltas); +@y + begin do_all_eight(update_width); + do_all_eight(combine_two_deltas); +@z + +@x + begin do_all_six(update_active); + do_all_six(copy_to_cur_active); +@y + begin do_all_eight(update_active); + do_all_eight(copy_to_cur_active); +@z + +@x +do_all_six(store_background);@/ +@y +do_all_eight(store_background);@/ +@z + +@x +begin prev_p:=cur_p; +repeat f:=font(cur_p); +act_width:=act_width+char_width(f)(char_info(f)(character(cur_p))); +cur_p:=link(cur_p); +until not is_char_node(cur_p); +end +@y +begin prev_p:=cur_p; +repeat f:=font(cur_p); +w:=char_width(f)(char_info(f)(character(cur_p))); +act_width:=act_width+w; +if hz_en then @<Update stretch and shrink in the presence of font variants@>; +cur_p:=link(cur_p); +until not is_char_node(cur_p); +end + +@ @<Local variables for line...@>= +@!w:scaled; {used when calculating character widths} + +@ @<Update stretch and shrink...@>= +begin +r:=font_variants[f]; +active_width[7]:=active_width[7]+ + char_width(font(r))(char_info(font(r))(character(cur_p)))-w; +while link(r)<>null do r:=link(r); +active_width[8]:=active_width[8]+ + w-char_width(font(r))(char_info(font(r))(character(cur_p))); +end +@z + +@x +def_font: new_font(a); +@y +def_font: if cur_chr=0 then new_font(a) else + if hz_en then font_variant(a) + else begin print_err("Improper "); print_cmd_chr(def_font,cur_chr); error; end; + + +@ @<Declare subprocedures for |prefixed_command|@>= +procedure font_variant(@!a:small_number); +label found,done; +var @!f,ff:internal_font_number; +@!i:eight_bits; +@!qw,qww:four_quarters; +@!r,s,p: pointer; +begin + scan_font_ident; f:=cur_val; + scan_optional_equals; scan_font_ident; ff:=cur_val; + @<If |ff| is |null_font|, reset the list of variants and goto |found|@>; + @<Check that |ff| is a valid variant of |f|, else goto |done|@>; + s:=null; r:=font_variants[f]; + i:=character(r); + p:=new_character(ff,i); + qww:=char_info(ff)(i); + while r<>null do begin + if font(r)=ff then goto done; + qw:=char_info(font(r))(i); + if char_width(font(r))(qw)<char_width(ff)(qww) then goto found; + s:=r; r:=link(r); + end; +found: + link(p):=r; + if s=null then font_variants[f]:=p + else link(s):=p; + done: +@!debug + print("(fontvariants of "); + print(font_name[f]); print(": "); + r:=font_variants[f]; + while r<>null do begin + print(font_name[font(r)]); + r:=link(r); + if r<>null then print(", "); + end; + print(")"); +gubed +end; + +@ Fonts can only be variants of each other if they provide the +same characters. + +@<Check that |ff| is a valid variant of |f|...@>= +for i:=0 to 255 do begin + qw:=char_info(f)(i); + qww:=char_info(f)(i); + if (char_exists(qw) and not char_exists(qww)) or + (not char_exists(qw) and char_exists(qww)) then begin + print_err("Font "); print_esc(font_id_text(ff)); + print(" is not a variant of font "); print_esc(font_id_text(f)); +@.Font x is not a...@> + help2("Fonts can only be used as variants of each other")@/ + ("if they contain the same characters."); + error; + goto done; + end; +end + +@ The only way to remove variants of a font is to assign |null_font| +as a variant. + +@<If |ff| is |null_font|...@>= +if ff=null_font then begin + r:=null; s:=null; p:=new_character(f,character(font_variants[f])); + flush_node_list(font_variants[f]); + goto found; +end +@z + diff --git a/systems/tex-extensions/clasen/hz/hz.tex b/systems/tex-extensions/clasen/hz/hz.tex new file mode 100644 index 0000000000..3ff4432261 --- /dev/null +++ b/systems/tex-extensions/clasen/hz/hz.tex @@ -0,0 +1,104 @@ +\documentclass[preprint]{ltugboat} +\usepackage{shortvrb} +\usepackage{url} + +\providecommand{\meta}[1]{\ensuremath{\langle\textit{#1}\rangle}} +\makeatletter +\providecommand{\eTeX}{$\m@th\varepsilon$-\TeX} +\makeatother +\hyphenation{pa-ra-me-ter} + +\emergencystretch=4mm +\overfullrule=0pt +\title{Why \TeX\ can't set the Gutenberg bible} +\author{Matthias Clasen} +%\address{} +\netaddress{maclas@gmx.de} +\setcounter{page}{1} + +\begin{document} +\maketitle +\MakeShortVerb{\|} + +\section{Introduction} + +\TeX's paragraph breaking algorithm is one of its virtues. It usually +achieves a fairly uniform grey value throughout the page. If it can't achieve +completely uniform spacing, the excess space is distributed among the +interword spaces in the line. + +Thus \TeX\ doesn't use one technique for uniform spacing which was invented +as far back as Gutenberg: variation of the letter width to make up excess +space. + +I propose a minimalist extension of \TeX\ to enable it to use letters of +variable width. The extension is to associate \textit{variants} with +fonts. If \TeX\ is about to set character~|c| from font~|f|, it looks for +character~|c| in all variants associated with~|f| and chooses the one +which ``fits best in the current situation''. I.\,e.\ in a tight line, +a narrow variant will be chosen, while a wide variant would be preferable +in a loose line. The variability of the character width also influences the +calculation of the badness during the paragraph breaking. This is the true +strength of the extension: like |\emergencystretch|, it adds flexibility +during paragraph breaking and thus increases the number of feasible +breakpoints, but without tearing ugly holes in the shape of the paragraph. + +I call this a minimalist extension, because it completely ignores the +questions of suitable font technology for automatic generation of font +variants from existing fonts. From the point of view taken here, font +variants are just separate |tfm| files which are only required to contain +characters in the same positions. Furthermore \TeX\ assumes that the +widths vary consistently between the variants, i.\,e.\ if variant~|f| +contains \emph{one} character which is wider than the corresponding +character in variant~|f'|, then \emph{all} characters from~|f| should +be at least as wide as their counterparts in~|f'|. + +\section{Implementation} + +The changes to \TeX{} the program sketched above are realized in the +change file |hz.ch|. It implements the new primitive +\[\cs{fontvariant}\meta{font$_1$}=\meta{font$_2$}\] +which declares \meta{font$_2$} to be a variant of \meta{font$_1$} +(where \meta{font$_1$} and \meta{font$_2$} must be font identifiers). +Font variants can only be removed \textit{en bloc}, by using +|\nullfont| as \meta{font$_2$}. + +\TeX\ will use the fontvariants and allow the |\fontvariant| primitive only +when the new integer parameter |\hzstate| has a positive value. Setting +|\hzstate| to zero does \emph{not} remove the font variants previously +declared. + +\TeX\ uses the font variants only when it looks for the width of a character. +All other |tfm| informations, in particular kerning and ligaturing, are always +taken from the base font. + +\section{Installation} + + The change files are written for \TeX~3.1459. But it should need only + cosmetic changes to get them to work with other versions of \TeX3. + + To make the change files for \TeX{} work, you have to apply the change file + |pre.ch| first. I have only tried these files with |web2c|, and + for that you need to surround the |web2c| change file with |pre-web2c.ch| and + |post-web2c.ch|. You also need the additional change file + |hz-post-web2c.ch|. If you want to use this change together with the + change for fixed-point arithmetic (|glue.ch|), then you also need + the file |hz-glue.ch|. + + All this boils down to the command line + +\begin{verbatim} +mv tex.ch tex.ch.orig +tie -c tex.ch tex.web pre.ch hz.ch \ + hz-glue.ch pre-web2c.ch \ + tex.ch.orig post-web2c.ch \ + hz-post-web2c.ch +\end{verbatim} + + \makesignature + + \bibliographystyle{plain} + \bibliography{etex} + +\end{document} + |