[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Fix of double setkern bug



The merging of my fix of the double \setkern bug in fontinst with fontinst
v1.801 has now been completed. What follows here are the changed parts of
fontinst.dtx, together with some comments.

I have have tested the code myself, but of course that is no guarantee that
there are no errors in it. The new commands should work as promised,
however.

Section 7:

As some of the new code is not specific to the place where it is used, I
put it here rather than the sections most affected. I also took the
opportunity to make a slight optimisation of an \ifnum in \resetkern.

%
% \section{\TeX{} hackery}
%
% \subsection{Utiltiy macros}
%
% \begin{macro}{\x_cs}
% \begin{macro}{\x_relax}
% \begin{macro}{\g_let}
%    \begin{macrocode}
\def\x_cs#1#2{\expandafter#1\csname#2\endcsname}
\let\x_relax=\relax
\def\g_let{\global\let}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\empty_command}
% \begin{macro}{\gobble_one}
% \begin{macro}{\gobble_two}
% \begin{macro}{\gobble_three}
% \begin{macro}{\identity_one}
% \begin{macro}{\first_of_two}
% \begin{macro}{\second_of_two}
%    \begin{macrocode}
\def\empty_command{}
\def\gobble_one#1{}
\def\gobble_two#1#2{}
\def\gobble_three#1#2#3{}
\def\identity_one#1{#1}
\def\first_of_two#1#2{#1}
\def\second_of_two#1#2{#2}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\percent_char}
% \begin{macro}{\left_brace_char}
% \begin{macro}{\right_brace_char}
%    \begin{macrocode}
\bgroup
   \catcode`\[=1
   \catcode`\]=2
   \catcode`\%=12
   \catcode`\{=12
   \catcode`\}=12
   \gdef\percent_char[%]
   \gdef\left_brace_char[{]
   \gdef\right_brace_char[}]
\egroup
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\lose_measure}
% Get rid of a trailing |pt| when converting dimension.
%    \begin{macrocode}
{
   \catcode`\p=12
   \catcode`\t=12
   \gdef\lose_measure#1pt{#1}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\first_char}
% Return the first character of a string.
%
%    \begin{macrocode}
\def\first_char#1#2={#1}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\add_to} ^^A <LH MODIFICATION> (comment)
%   Append one or more tokens to the replacement text of a
%   parameterless macro.
%    \begin{macrocode}
\def\add_to#1#2{
   \ifx#1\relax
      \def#1{#2}
   \else
      \expandafter\def\expandafter#1\expandafter{#1#2}
   \fi
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\prep_to} ^^A <LH MODIFICATION>
%   Prepend one or more tokens to the replacement text of a
%   parameterless macro. Note that if more than one token is added then
%   the second parameter must contain an |\expandafter| between every
%   pair of tokens you actually mean to contribute. Thus if you want
%   to prepend |abc| to |\next|, you must write
%   \begin{quote}
%     |\prep_to\next{a\expandafter b\expandafter c}|
%   \end{quote}
%   Also note that the second argument must not be empty.
%    \begin{macrocode}
\def\prep_to#1#2{
   \ifx#1\relax
      \expandafter\def\expandafter#1\expandafter{\expandafter#2}
   \else
      \expandafter\def\expandafter#1\expandafter{\expandafter#2#1}
   \fi
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\never_do}
% The command |\do|, protected from expansion.
%
%    \begin{macrocode}
\def\never_do{\noexpand\do}
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Writing to \texttt{.fd} files}
%
% \begin{macro}{\open_out}
% \begin{macro}{\close_out}
% \begin{macro}{\out_line}
% \begin{macro}{\out_lline}
% \begin{macro}{\out_llline}
%    \begin{macrocode}
\def\open_out#1{
   \immediate\openout\out_file=#1 \def\out_filename{#1}}
\def\close_out#1{
   \immediate\write16{#1~written~on~\out_filename.}
   \immediate\closeout\out_file}
\def\out_line#1{\immediate\write\out_file{#1}}
\def\out_lline#1{\out_line{\space\space\space#1}}
\def\out_llline#1{\out_lline{\space\space\space#1}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% \subsection{Conditionals}
%
% \begin{macro}{\if_true}
% \begin{macro}{\if_false}
%
% In order to write macros that expand out to nested |\if|-statements,
% I say:
% \begin{quote}
%    |\ifblah...\then...\else...\fi|
% \end{quote}
% In order to match the |\fi|, |\then| has to be an |\if|.
%
%    \begin{macrocode}
\let\then=\iffalse
\def\if_false{\iffalse}
\def\if_true{\iftrue}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% |\if_or...\or_else...\then| gives the disjunction of two booleans.
%
%    \begin{macrocode}
\def\if_or#1\or_else#2\then{
   #1\then
      \expandafter\if_true
   \else
      #2\then
         \expandafter\expandafter\expandafter\if_true
      \else
         \expandafter\expandafter\expandafter\if_false
      \fi
   \fi
}
%    \end{macrocode}
%
% \begin{macro}{\if_file_exists}
%
% |\if_file_exists| checks to see if a file exits, using |\openin|.
%
%    \begin{macrocode}
\def\if_file_exists#1\then{
   \immediate\openin1=#1\relax
   \ifeof1\relax
      \immediate\closein1
      \expandafter\if_false
   \else
      \immediate\closein1
      \expandafter\if_true
   \fi
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\primitiveinput}
%
% If |\@@input| is defined, I'll assume it's the \LaTeX{} version
% of the \TeX{} |\input| primitive.  I need this so that I can say
% |\expandafter\primitiveinput foo|, which doesn't work with the
% \LaTeX{} version of |\input|.
%
%    \begin{macrocode}
\x_cs\ifx{@@input}\relax
   \let\primitiveinput=\input
\else
   \let\primitiveinput=\@@input
\fi
%    \end{macrocode}
%  \end{macro}
%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \subsection{Comma-separated lists}
%
% fontinst is sometimes given a comma-separated list of items in which
% each item is to be processed separately. This subsection defines a
% generic macro for doing this.
%
% \DescribeMacro\process_csep_list
% The macro
% \begin{quote}
%   |\process_csep_list{|\meta{pretext}|}|\meta{comma-sep~list}%
%      |,\process_csep_list,|
% \end{quote}
% executes \meta{pretext}|{|\meta{item}|}| for every item in the
% \meta{comma-sep~list}.
%
% \begin{macro}{\process_csep_list}
%    \begin{macrocode}
\def\process_csep_list#1#2,{
   \ifx\process_csep_list#2
      \expandafter\gobble_one
   \else
      \expandafter\identity_one
   \fi{
      #1{#2}
      \process_csep_list{#1}
   }
}
%    \end{macrocode}
% \end{macro}
%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \subsection{Setting variables}
%
% \DescribeMacro{\setint}
% \DescribeMacro{\setstr}
% \DescribeMacro{\setdim}
% \DescribeMacro{\setcommand}
% The macros:
% \begin{quote}
%    |\setint{INT}{INTEGER EXPRESSION}|\\
%    |\setstr{STR}{STRING}|\\
%    |\setdim{DIM}{DIMENSION}|\\
%    |\setcommand \COMMAND DEFINITION|
% \end{quote}
% define new macros |\i-INT|, |\s-STR|, |\d-DIM| or |\COMMAND|.
%
% \DescribeMacro{\resetint}
% \DescribeMacro{\resetstr}
% \DescribeMacro{\resetdim}
% \DescribeMacro{\resetcommand}
% The macros:
% \begin{quote}
%    |\resetint{INT}{INTEGER EXPRESSION}|\\
%    |\resetstr{STR}{STRING}|\\
%    |\resetdim{DIM}{DIMENSION}|\\
%    |\resetcommand \COMMAND DEFINITION|
% \end{quote}
% redefine the macros |\i-INT|, |\s-STR|, |\d-DIM| or |\COMMAND|.
%
% \DescribeMacro{\int}
% \DescribeMacro{\str}
% \DescribeMacro{\dim}
% The macros:
% \begin{quote}
%    |\int{INT}|\\
%    |\str{STR}|\\
%    |\dim{DIM}|\\
%    |\COMMAND|
% \end{quote}
% return the values of |\i-INT|, |\s-STR|, |\d-DIM| or |\COMMAND|.
%
% \DescribeMacro{\ifisint}
% \DescribeMacro{\ifisstr}
% \DescribeMacro{\ifisdim}
% \DescribeMacro{\ifiscommand}
% The macros:
% \begin{quote}
%    |\ifisint{INT}\then|\\
%    |\ifisstr{STR}\then|\\
%    |\ifisdim{DIM}\then|\\
%    |\ifiscommand\COMMAND\then|
% \end{quote}
% return |\if_true| if |\i-INT|, |\s-STR| or |\d-DIM| have been defined,
% and |\if_false| otherwise.
%
% \DescribeMacro{\unsetint}
% \DescribeMacro{\unsetstr}
% \DescribeMacro{\unsetdim}
% \DescribeMacro{\unsetcommand}
% The macros:
% \begin{quote}
%    |\unsetint{INT}|\\
%    |\unsetstr{STR}|\\
%    |\unsetdim{DIM}|\\
%    |\unsetcommand \COMMAND|
% \end{quote}
% undefine |\i-INT|, |\s-STR|, |\d-DIM| or |\COMMAND|.
%
% Integers are kept as |\mathchardef|s if possible.  I compared the
% version where you try to keep ints as |\mathchardef|s with the version
% where you don't bother, and for a sample font without |\mathchardef|s
% I got:
% \begin{quote}
%   114673 words of memory out of 150001\\
%   Time elapsed: 135.0 seconds
% \end{quote}
% and with, I got:
% \begin{quote}
%   114050 words of memory out of 150001\\
%   Time elapsed: 134.5 seconds
% \end{quote}
% so I've saved a little memory and time.  Not brilliant, but I may as
% well keep it in.
%
% \begin{macro}{\setint}
% \begin{macro}{\setstr}
% \begin{macro}{\setdim}
% \begin{macro}{\setcommand}
%    \begin{macrocode}
\def\setint#1#2{
   \x_cs\ifx{i-#1}\relax
      \resetint{#1}{#2}
   \fi
}
\def\setstr#1#2{\x_cs\ifx{s-#1}\relax
   \x_cs\edef{s-#1}{#2}\fi}
\def\setdim#1#2{\x_cs\ifx{d-#1}\relax
   \a_dimen=#2\relax\x_cs\edef{d-#1}{\the\a_dimen}\fi}
\def\setcommand#1{\ifx#1\undefined_command
   \expandafter\def\expandafter#1\else
   \expandafter\def\expandafter\a_command\fi}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\resetint}
% \begin{macro}{\resetstr}
% \begin{macro}{\resetdim}
% \begin{macro}{\resetcommand}
%    \begin{macrocode}
\def\resetint#1#2{
   \eval_expr{#2}
   \ifnum\result<\max_mathchardef
      \ifnum 0>\result % LH 1998/09/30
         \x_cs\edef{i-#1}{\the\result}
      \else
         \x_cs\mathchardef{i-#1}=\result
      \fi
   \else
      \x_cs\edef{i-#1}{\the\result}
   \fi
}
\def\resetstr#1#2{\x_cs\edef{s-#1}{#2}}
\def\resetdim#1#2{\a_dimen=#2\x_cs\edef{d-#1}{\the\a_dimen}}
\def\resetcommand#1{\def#1}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\int}
% \begin{macro}{\str}
% \begin{macro}{\dim}
%    \begin{macrocode}
\def\int#1{\csname~i-#1\endcsname}
\def\str#1{\csname~s-#1\endcsname}
\def\dim#1{\csname~d-#1\endcsname}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\ifisint}
% \begin{macro}{\ifisstr}
% \begin{macro}{\ifisdim}
% \begin{macro}{\ifiscommand}
%    \begin{macrocode}
\def\ifisint#1\then{\x_cs\ifx{i-#1}\relax\expandafter\if_false
   \else\expandafter\if_true\fi}
\def\ifisstr#1\then{\x_cs\ifx{s-#1}\relax\expandafter\if_false
   \else\expandafter\if_true\fi}
\def\ifisdim#1\then{\x_cs\ifx{d-#1}\relax\expandafter\if_false
   \else\expandafter\if_true\fi}
\def\ifiscommand#1\then{\ifx#1\undefined_command\expandafter\if_false
   \else\expandafter\if_true\fi}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\unsetint}
% \begin{macro}{\unsetstr}
% \begin{macro}{\unsetdim}
% \begin{macro}{\unsetcommand}
%    \begin{macrocode}
\def\unsetint#1{\x_cs\let{i-#1}\relax}
\def\unsetstr#1{\x_cs\let{s-#1}\relax}
\def\unsetdim#1{\x_cs\let{d-#1}\relax}
\def\unsetcommand#1{\let#1=\undefined_command}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% \subsection{Integer expressions}
%
% \begin{macro}{\eval_expr}
% \begin{macro}{\eval_expr_to}
% \begin{macro}{\g_eval_expr_to}
% The macro:
% \begin{quote}
%    |\eval_expr{INTEGER EXPRESSION}|
% \end{quote}
% globally assigns |\result| to the value of |INTEGER EXPRESSION|,
% and changes the value of no other counters.
%
% The macro:
% \begin{quote}
%    |\eval_expr_to{VARIABLE}{INTEGER EXPRESSION}|
% \end{quote}
% locally assigns the value of |INTEGER EXPRESSION| to |VARIABLE|.
% |\g_eval_expr_to| does the same globally.
%
%    \begin{macrocode}
\newcount\result
\def\eval_expr#1{\global\result=#1\relax}
\def\eval_expr_to#1#2{\eval_expr{#2}#1=\result}
\def\g_eval_expr_to#1#2{\eval_expr{#2}\global#1=\result}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\neg}
% \begin{macro}{\add}
% \begin{macro}{\sub}
% \begin{macro}{\mul}
% \begin{macro}{\div}
% \begin{macro}{\max}
% \begin{macro}{\min}
% \begin{macro}{\scale}
% These macros return an integer expression:
% \begin{quote}
%    |\neg{INTEGER EXPRESSION}|\\
%    |\add{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\sub{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\mul{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\div{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\max{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\min{INTEGER EXPRESSION}{INTEGER EXPRESSION}|\\
%    |\scale{INTEGER EXPRESSION}{INTEGER EXPRESSION}|
% \end{quote}
%
%    \begin{macrocode}
\def\neg#1{#1\global\result=-\result}
\def\add#1#2{#1{\a_count=\result\eval_expr{#2}
   \global\advance\result by \a_count}}
\def\sub#1#2{#1{\a_count=\result\eval_expr{#2}
   \advance\a_count by -\result
   \global\result=\a_count}}
\def\mul#1#2{#1{\a_count=\result\eval_expr{#2}
   \global\multiply\result by \a_count}}
\def\div#1#2{#1{\a_count=\result\eval_expr{#2}
   \divide\a_count by \result
   \global\result=\a_count}}
\def\max#1#2{#1{\a_count=\result\eval_expr{#2}
   \ifnum\a_count>\result \global\result=\a_count \fi}}
\def\min#1#2{#1{\a_count=\result\eval_expr{#2}
   \ifnum\a_count<\result \global\result=\a_count \fi}}
\def\scale#1#2{#1{\a_count=\result\eval_expr{#2}
   \global\multiply\result by \a_count
   \global\divide\result by \one_thousand}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
% \end{macro}
%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

Subsection 10.1:

This is where the new commands appear: \resetkern, \unsetkerns, and
\ifiskern. \resetkern has the same run-time and memory requirements as the
normal \setkern. You may read about \unsetkerns yourself. \ifiskern was
included only to keep the symmetry with ints, dimens, strings, and glyphs.

% \subsection{Kerning information}
%
% The kerning information is kept in the macros |\l-NAME| and
% |\r-NAME|, containing information about how |NAME| kerns on the left
% and on the right, respectively.  The |\l-NAME| macro should expand
% out to a series of |\k\r-NAME\AMOUNT| macros, and vice versa.
%
% Where possible, we avoid re-scaling, which saves a bit of time and
% memory.  With a sample font, the version where we didn't avoid
% re-scaling used:
% \begin{quote}
%   114050 words of memory out of 150001\\
%   Time elapsed: 134.5 seconds
% \end{quote}
% whereas the version where we do avoid it used:
% \begin{quote}
%   113786 words of memory out of 150001\\
%   Time elapsed: 124.9 seconds
% \end{quote}
%
% We keep the names of the glyphs to kern with as |\l-NAME| and
% |\r-NAME| to save on token space, and this got the resources used
% down to:
% \begin{quote}
%   88574 words of memory out of 150001\\
%   Time elapsed: 106.1 seconds
% \end{quote}
% Keeping track of the kern amounts as |\AMOUNT| got the resources
% used down to:
% \begin{quote}
%   75424 words of memory out of 150001\\
%   Time elapsed: 97.2 seconds
% \end{quote}
% Mind you, I then added all the |\transformfont| stuff, and it went
% back to:
% \begin{quote}
%   77079 words of memory out of 150001\\
%   Time elapsed: 97.7 seconds
% \end{quote}
%
%
% \begin{macro}{\setkern}
% |\setkern{GLYPH1}{GLYPH2}{INTEGER EXPERESSION}|
%
% Sets a kern pair between |GLYPH1| and |GLYPH2| to the specified value,
% which is typically a value returned by |\kerning{GLYPH3}{GLYPH4}|.
%
%    \begin{macrocode}
\def\setkern#1#2#3{
   \x_cs\ifx{i-rawscale}\relax
      \expandafter\set_kern
         \csname~r-#1\expandafter\endcsname
         \csname~l-#2\endcsname{#3}
   \else
      \expandafter\set_kern
         \csname~r-#1\expandafter\endcsname
         \csname~l-#2\endcsname{\scale{#3}{\int{rawscale}}}
   \fi
}
\def\set_kern#1#2#3{
   \eval_expr{#3}
   \expandafter\set_kern_cs\csname\the\result\endcsname#1#2
}
\def\set_kern_cs#1#2#3{
   \add_to#2{\k#3#1}
   \add_to#3{\k#2#1}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\resetkern}
%   |\resetkern{GLYPH1}{GLYPH2}{INTEGER EXPERESSION}|
%
%   Resets the kern pair between |GLYPH1| and |GLYPH2| to the specified
%   value.
%
%    \begin{macrocode}
\def\resetkern#1#2#3{
   \x_cs\ifx{i-rawscale}\relax
      \expandafter\reset_kern
         \csname~r-#1\expandafter\endcsname
         \csname~l-#2\endcsname{#3}
   \else
      \expandafter\reset_kern
         \csname~r-#1\expandafter\endcsname
         \csname~l-#2\endcsname{\scale{#3}{\int{rawscale}}}
   \fi
}
\def\reset_kern#1#2#3{
   \eval_expr{#3}
   \expandafter\reset_kern_cs\csname\the\result\endcsname#1#2
}
\def\reset_kern_cs#1#2#3{
   \prep_to#2{\k\expandafter#3\expandafter#1}
   \prep_to#3{\k\expandafter#2\expandafter#1}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\setleftkerning}
% \begin{macro}{\setrightkerning}
% \begin{macro}{\setleftrightkerning}
% |\setleftkerning{GLYPH1}{GLYPH2}{SCALED}|\\
% |\setrightkerning{GLYPH1}{GLYPH2}{SCALED}|\\
% |\setleftrightkerning{GLYPH1}{GLYPH2}{SCALED}|
%
% Sets left or right kerning of |GLYPH1| to that of |GLYPH2|
% scaled by |SCALED|.  |\setleftrightkerning| does both.
%
%    \begin{macrocode}
\def\setleftkerning#1#2#3{
   \eval_expr_to\b_count{#3}
   \expandafter\set_kerning
      \csname~l-#1\expandafter\endcsname
      \csname~l-#2\endcsname
}
\def\setrightkerning#1#2#3{
   \eval_expr_to\b_count{#3}
   \expandafter\set_kerning
      \csname~r-#1\expandafter\endcsname
      \csname~r-#2\endcsname
}
\def\setleftrightkerning#1#2#3{
   \eval_expr_to\b_count{#3}
   \expandafter\set_kerning
      \csname~l-#1\expandafter\endcsname
      \csname~l-#2\endcsname
   \expandafter\set_kerning
      \csname~r-#1\expandafter\endcsname
      \csname~r-#2\endcsname
}
\def\set_kerning#1#2{
   \if\b_count=\one_thousand
      \def\k##1##2{
         \set_kern_cs##2##1#1
      }
   \else
      \def\k##1##2{
         \set_kern##1#1{
            \scale\b_count{\expandafter\gobble_one\string##2}
         }
      }
   \fi
   #2
}
%    \end{macrocode}
% \end{macro}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\kerning}
% |\kerning{GLYPH1}{GLYPH2}|
%
% Returns the value of kern pair between |GLYPH1| and |GLYPH2| as an
% integer.  Returns a value of zero if such a kern pair doesn't exist.
%
%    \begin{macrocode}
\def\kerning#1#2{0\relax
   \def\k##1{\csname~set-\string##1\endcsname\gobble_one}
   \bgroup
   \x_cs\def{set-\string\l-#2}##1##2{
      \global\result=\expandafter\gobble_one\string##2\egroup
   }
   \csname~r-#1\endcsname
   \csname~set-\string\l-#2\endcsname\gobble_one{00}
}
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\ifiskern}
% |\ifiskern{|\meta{glyph1}|}{|\meta{glyph2}|}\then|
%
% This command tests if there is a kern pair between \meta{glyph1} and
% \meta{glyph2}. It's hard to say if there is a use for it, but it is
% included for symmetry.
%
%    \begin{macrocode}
\def\ifiskern#1#2\then{
   \def\k##1##2{\ifx T##1 \let\k\gobble_two \fi}
   \bgroup
      \x_cs\let{l-#2}T
      \csname r-#1\endcsname
   \expandafter\egroup \ifx\k\gobble_two
}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\unsetkerns}
%   |\unsetkerns{|\meta{left~glyph~list}|}{|\meta{right~glyph~list}|}|
%
%   This command unsets all kerns with a glyph in the \meta{left~glyph~list}
%   on the left and a glyph in the \meta{right~glyph~list} on the right.
%   The lists themselves are ordinary comma-separated lists.
%
%   The implementation itself simply goes through |\r-|\meta{left~glyph}
%   for each element \meta{left~glyph} in \meta{left~glyph~list} and
%   |\l-|\meta{right~glyph} for each element \meta{right~glyph} in
%   \meta{right~glyph~list}, removing each |\k|\meta{token}\meta{token}
%   tripple that refers to a glyph from the opposite list as it goes
%   along. To make this test reasonably fast, the routine first ``marks''
%   the glyphs in the other list by setting the control sequences
%   |\i-|\meta{glyph} to |U| (the letter token U). This mark is later
%   removed when the |\r-|\meta{glyph} or |\l-|\meta{glyph} respectively
%   control sequences have been gone through; the |\i-|\meta{glyph}
%   control sequences are then set to |\relax|.
%
%   As these control sequences are normally used for integers, this could
%   unintentionally unset some integers as a side-effect, but the integers
%   whose names are the names of glyphs are special since these are
%   assumed to store the slot numbers for these glyphs when the (V)PL
%   file is written. The metric file commands should not set them at all.
%   The reason for choosing them here is to conserve space in some of
%   \TeX's internal tables.
%
%    \begin{macrocode}
\def\unsetkerns#1#2{
   \let\k\k_unless_to_U
   \process_csep_list\make_int_U#1,\process_csep_list,
   \def\do##1{\x_cs\main_remove_Us{l-##1}}
   \process_csep_list\do#2,\process_csep_list,
   \process_csep_list\unsetint#1,\process_csep_list,
   \process_csep_list\make_int_U#2,\process_csep_list,
   \def\do##1{\x_cs\main_remove_Us{r-##1}}
   \process_csep_list\do#1,\process_csep_list,
   \process_csep_list\unsetint#2,\process_csep_list,
}
\def\make_int_U#1{\x_cs\let{i-#1}U}
\def\k_unless_to_U#1#2{
   \x_cs\ifx{i-\expandafter\gobble_three\string#1}U \else
      \noexpand\k\noexpand#1\noexpand#2
   \fi
}
\def\main_remove_Us#1{
   \ifx#1\relax \else
      \edef#1{#1}
      \ifx#1\empty_command \let#1\relax \fi
   \fi
}
%    \end{macrocode}
% \end{macro}
%
%


Subsection 11.1:

This is the part that has changed the most, although changes needed for the
bug fix were rather small. I took the liberty to reorganise much of the
code in this subsection as it appears in the .dtx file, so that
\make_mapfonts, \make_ligtable, and \make_characters are close to the
macros they refer to rather than to each other. I also included
\subsubsection headings in this subsection to assist the reader in finding
where he/she is (Subsection 11.1 is a _long_ subsection). I recommend
setting the tocdepth counter to 3 so these subsubsections do not show up in
the table of contents.

The actual bugfix affected \make_header, \make_ligtable, \vpl_kerning, and
\vpl_kern. I think the comments in the file explains it best.

I also changed the mechanism for suppressing MAP and MAPFONTS property
lists when \etxtopl is called rather than \etxtovpl. It now uses the save
stack a bit more efficiently. This part is also commented.

Finally I have added a series of hooks into the entire mechanism, as I
requested some while back. Personally, I have use for at least two of these
in my multislot extension of fontinst. There are of course comments on how
to use these as well.

% \subsection{Converting an \texttt{ETX} file to a \texttt{(V)PL} file}
%
% \subsubsection{Lowest-level user interface}
%
% \DescribeMacro{\etxtovpl}
% The macro:
% \begin{quote}
%    |\etxtovpl{ENCODING}{VPLFILE}|
% \end{quote}
% writes a virtual font (as a virtual property list) with the encoding
% |ENCODING|.  (This macro is called by |\installfont|.)
%
% \DescribeMacro{\etxtopl}
% The macro:
% \begin{quote}
%    |\etxtopl{ENCODING}{PLFILE}|
% \end{quote}
% writes a font (as a property list) with the encoding |ENCODING|.
% (This macro is called by |\installrawfont|.)
%
% \begin{macro}{\etxtovpl}
% \begin{macro}{\etxtopl}
%    \begin{macrocode}
\def\etxtovpl#1#2{{
   \def\vpl_extension{vpl}
   \def\vpl_title{COMMENT}
   \def\vpl_font{virtual~font}
   \def\vpl_Font{Virtual~font}
   \def\vpl_caller{\string\etxtovpl}
   \def\vpl_to_vf##1{vpltovf~##1.vpl~##1.vf~##1.tfm}
   \etx_to_font{#1}{#2}
}}
\def\etxtopl#1#2{{
   \def\vpl_extension{pl}
   \def\vpl_title{COMMENT}
   \def\vpl_font{font}
   \def\vpl_Font{Font}
   \def\vpl_caller{\string\etxtopl}
   \def\vpl_to_vf##1{pltotfm~##1.pl~##1.tfm}
   \_including_mapfalse
   \etx_to_font{#1}{#2}
}}
%    \end{macrocode}
% \end{macro}
% \end{macro}
%
% \begin{macro}{\if_including_map}
%   LH 1998/10/02: The v1.5 method of controlling whether \texttt{MAP}
%   and \texttt{MAPFONT} property lists should be written requires that
%   some code is put in an awkward place (in |\make_mapfonts| rather
%   than |\make_characters|). As I am reorganising the code anyway, I
%   saw it best to let a switch control this instead. If
%   |\if_including_map| is true, \texttt{MAP} and \texttt{MAPFONT}
%   property lists are written to the file (which should be a VPL), if
%   it is false then they are not written to the file (which is then a
%   normal PL).
%    \begin{macrocode}
\newif\if_including_map
\_including_maptrue
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Hooks into the (\texttt{V})\texttt{PL} file generation}
%
% \begin{macro}{\pre_first_etx_pass_hook}
% \begin{macro}{\pre_second_etx_pass_hook}
% \begin{macro}{\pre_third_etx_pass_hook}
% \begin{macro}{\pre_fourth_etx_pass_hook}
% \begin{macro}{\post_first_etx_pass_hook}
% \begin{macro}{\post_second_etx_pass_hook}
% \begin{macro}{\post_third_etx_pass_hook}
% \begin{macro}{\post_fourth_etx_pass_hook}
% \begin{macro}{\tidying_up_hook}
%   LH 1998/09/30: As the |\etxtovpl| and |\etxtopl| macros are the
%   lowest-level interfaces to what they do that are available in fontinst
%   and as the amount of code they execute is really quite large, I feel
%   there is a need for some robust mechanism for adding code to this,
%   in case the need should arise. Such a mechanism is provided by the
%   following hooks:
%    \begin{macrocode}
\let\pre_first_etx_pass_hook\relax
\let\pre_second_etx_pass_hook\relax
\let\pre_third_etx_pass_hook\relax
\let\pre_fourth_etx_pass_hook\relax
\let\post_first_etx_pass_hook\relax
\let\post_second_etx_pass_hook\relax
\let\post_third_etx_pass_hook\relax
\let\post_fourth_etx_pass_hook\relax
\let\tidying_up_hook\relax
%    \end{macrocode}
%   To include code in one of them, one should write things like
%   \begin{quote}
%     |\add_to\pre_first_etx_pass_hook{|\meta{extra~code}|}|
%   \end{quote}
% \end{macro} \end{macro} \end{macro} \end{macro} \end{macro}
% \end{macro} \end{macro} \end{macro} \end{macro}
%
%
% \subsubsection{Glyph to slot assignments; the Big Picture}
%
% \begin{macro}{\etx_to_vpl}
%    \begin{macrocode}
\def\etx_to_font#1#2{
   \def\do_slot{\resetint\slot_name\slot_number}
   \pre_first_etx_pass_hook
   \inputetx{#1}
   \post_first_etx_pass_hook
   \open_out{#2.\vpl_extension}
   \out_line{(\vpl_title\space\vpl_font\space
      #2~created~by~fontinst~v\fontinstversion)}
   \out_line{}
   \out_line{(COMMENT~Filename:~#2.\vpl_extension)}
   \out_line{(COMMENT~Created~by:~tex~\jobname)}
   \out_line{(COMMENT~Created~using:~\vpl_caller{#1}{#2})}
   \out_line{}
   \out_line{(COMMENT~This~file~can~be~turned~into~a~\vpl_font\space with)}
   \out_line{(COMMENT~\vpl_to_vf{#2})}
   \out_line{}
   \out_line{(COMMENT~THIS~FILE~CAN~THEN~BE~DELETED.)}
   \out_line{}
   \make_font{#1}
   \out_line{}
   \out_line{(COMMENT~END~OF~FILE~#2.\vpl_extension)}
   \close_out{\vpl_Font}
}
%    \end{macrocode}
% \end{macro}
%
% |\make_font{ENCODING}| makes a |.vpl| file.
%
%    \begin{macrocode}
\def\make_font#1{
   \make_header{#1}
   \if_including_map \make_mapfonts{#1} \fi
   \make_fontdimens{#1}
   \make_ligtable{#1}
   \make_characters{#1}
   \make_tidy{#1}
}
%    \end{macrocode}
%
%
% \subsubsection{(\texttt{V})\texttt{PL} dimensions}
%
% |\afmconvert\DIMEN=INTEGER EXPRESSION;|
% converts a count into a dimen, assuming the count is a number
% of AFM units.  I'll assume that the largest dimension I'll have
% to deal with is 131pt, to try to minimize rounding errors.
%
%    \begin{macrocode}
\newdimen\scaled_design_size
%    \end{macrocode}
%
%    \begin{macrocode}
\def\afm_convert#1=#2;{
   \eval_expr{#2}
   #1=\scaled_design_size
   \divide#1 by 8
   \multiply #1 by \result
   \divide #1 by \one_thousand
   \multiply#1 by 8
}
%    \end{macrocode}
%
% The commands |\vpl_real\dimen |or |\vpl_int\count| print a dimension or
% integer in (V)PL syntax.
%
%    \begin{macrocode}
\def\vpl_real#1{R~\expandafter\lose_measure\the#1}
\def\vpl_int#1{D~\the#1}
%    \end{macrocode}
%
%
% \subsubsection{(\texttt{V})\texttt{PL} file header}
%
%    \begin{macrocode}
\newcount\boundary_char
\newdimen\side_bearings
\newdimen\curr_bearings
%    \end{macrocode}
%
%    \begin{macrocode}
\def\make_header#1{
   \global\font_count=0
   \setdim{designsize}{10pt}
   \scaled_design_size=\dim{designsize}
   \out_line{(DESIGNSIZE~\vpl_real\scaled_design_size)}
   \out_line{(DESIGNUNITS~\vpl_real\scaled_design_size)}
   \setstr{codingscheme}{UNKNOWN}
   \out_line{(CODINGSCHEME~\str{codingscheme})}
   \ifisint{boundarychar}\then
      \boundary_char=\int{boundarychar}
      \out_line{(BOUNDARYCHAR~\vpl_int\boundary_char)}
   \else
      \boundary_char=-1
   \fi
   \setint{letterspacing}{0}
   \afm_convert\side_bearings=\div{\int{letterspacing}}{2};
   \ifdim\side_bearings=0pt
      \let\do_character_sidebearings=\relax
   \fi
   \setint{minimumkern}{0}
   \minimum_kern=\int{minimumkern}
   \out_line{}
}
%    \end{macrocode}
%
%
% \subsubsection{The mapfonts}
%
%    \begin{macrocode}
\def\make_mapfonts#1{
   \let\saved_scale\vpl_scale
   \let\saved_mapfont\vpl_mapfont
   \let\do_slot=\do_mapfont
   \pre_second_etx_pass_hook
   \inputetx{#1}
   \post_second_etx_pass_hook
   \out_line{}
}
%    \end{macrocode}
%
% |\do_mapfont| produces a |MAPFONT| entry for each font used by
% glyph |\slot_name|.
%
%    \begin{macrocode}
\def\do_mapfont{
   \ifisglyph\slot_name\then
      \mapfonts\slot_name
   \fi
}
%    \end{macrocode}
%
% The following commands can be used in a |MAPFONT|:
%
%    \begin{macrocode}
\def\vpl_scale#1#2{{
   \divide \scaled_design_size by 8
   \multiply \scaled_design_size by #1
   \divide \scaled_design_size by \one_thousand
   \multiply \scaled_design_size by 8
   #2
}}
%    \end{macrocode}
%    \begin{macrocode}
\def\vpl_mapfont#1#2{
   \a_dimen=#2
   \x_cs\ifx{#1-\the\scaled_design_size}\relax
      \x_cs\xdef{#1-\the\scaled_design_size}{\the\font_count}
      \x_cs\xdef{f-\the\font_count}{#1-\the\scaled_design_size}
      \out_line{(MAPFONT~\vpl_int\font_count\space
         (FONTNAME~#1)~
         (FONTDSIZE~\vpl_real\a_dimen)~
         (FONTAT~\vpl_real\scaled_design_size))}
      \global\advance\font_count by 1
   \fi
}
%    \end{macrocode}
%    \begin{macrocode}
\newcount\font_count
\newcount\next_mapfont
\newcount\prev_mapfont
%    \end{macrocode}
%
%
% \subsubsection{The fontdimens}
%
%    \begin{macrocode}
\def\make_fontdimens#1{
   \out_line{(FONTDIMEN}
      \ifisint{fontdimen(1)}\then
         \a_dimen=\int{fontdimen(1)}pt
         \divide\a_dimen by \one_thousand
         \out_lline{(PARAMETER~D~1~\vpl_real\a_dimen)}
      \fi
      \a_count=2
      \loop\ifnum\a_count<256
         {\ifisint{fontdimen(\the\a_count)}\then
            \afm_convert\a_dimen=\int{fontdimen(\the\a_count)};
            \out_lline{(PARAMETER~\vpl_int\a_count\space\vpl_real\a_dimen)}
         \fi}
      \advance\a_count by 1 \repeat
   \out_lline{)}
   \out_line{}
}
%    \end{macrocode}
%
%
% \subsubsection{The ligtable}
%
%    \begin{macrocode}
\def\make_ligtable#1{
   \bgroup
      \out_line{(LIGTABLE}
      \let\do_slot=\bgroup
      \let\end_do_slot=\vpl_kerning
      \let\ligature=\vpl_ligature
      \let\k=\vpl_kern
      \pre_third_etx_pass_hook
      \inputetx{#1}
      \post_third_etx_pass_hook
      \out_lline{)}
   \egroup
   \out_line{}
}
%    \end{macrocode}
%
% |\vpl_ligature{TYPE}{NAME}{NAME}| produces a ligtable entry for
% glyph |\slot_name|.
%
%    \begin{macrocode}
\def\vpl_ligature#1#2#3{
   \ifisint{#2}\then
      \ifisint{#3}\then
         \ifisglyph{#3}\then
            \a_count=\int{#2}
            \b_count=\int{#3}
            \vpl_liglabel
            \out_lline{(#1~\vpl_int\a_count\space \vpl_int\b_count)~
               (COMMENT~#2~#3)}
         \else
            \immediate\write16{Warning:~\string\ligature\space
               for~unknown~slot~`#3'.}
         \fi
      \else
         \immediate\write16{Warning:~\string\ligature\space
            for~unknown~slot~`#3'.}
      \fi
   \else
      \immediate\write16{Warning:~\string\ligature\space
         for~unknown~slot~`#2'.}
   \fi
}
%    \end{macrocode}
%
% |\vpl_kerning| writes out kerning instructions.
%
% LH 1998/10/02: The |\egroup| matches the |\bgroup| that |\do_slot|
% is |\let| to when this macro is used.
%
%    \begin{macrocode}
\def\vpl_kerning{\csname~r-\slot_name\endcsname \vpl_ligstop\egroup}
%    \end{macrocode}
%
% |\vpl_kern\l-NAME\AMOUNT| writes out a |KRN| instruction.
%
% LH 1998/10/02: |\vpl_kern| has been modified so that at most one
% \texttt{KRN} instruction is written for each (ordered) pair of
% characters. The idea is basically to make fontinst forget, until the
% end of |\vpl_kerning|, that the glyph for which |\vpl_kern| is being
% called has been assigned a slot, as this stops any additional \texttt{KRN}
% instructions for that particular glyph from being written. |\vpl_kern|
% has also been modified so that it will not write out any \texttt{KRN}
% instructions for kerns whose absolute value is less than or equal to
% |\minimum_kern|. |\minimum_kern| gets set to the value of the integer
% \texttt{minimumkern} in |\make_header|. If the user has not set
% \texttt{minimumkern}, a default value of 0 will be supplied by fontinst.
%
%    \begin{macrocode}
\def\vpl_kern#1#2{
   \edef\temp_command{\expandafter\gobble_three\string#1}
   \ifisint\temp_command\then
      \a_count=\expandafter\gobble_one\string#2\relax
      \ifnum -\a_count>\a_count
         \a_count=-\a_count
      \fi
      \ifnum \a_count>\minimum_kern
         \vpl_liglabel
         \a_count=\int\temp_command
         \afm_convert\a_dimen=\expandafter\gobble_one\string#2;
         \out_lline{
            (KRN~\vpl_int\a_count\space \vpl_real\a_dimen)~
            (COMMENT~\temp_command)
         }
      \fi
      \unsetint\temp_command
   \fi
}
%    \end{macrocode}
%
% |\vpl_liglabel| writes out a |LIGLABEL| instruction if appropriate.
%
%    \begin{macrocode}
\def\out_liglabel{
   \out_lline{(LABEL~\vpl_int\slot_number)~(COMMENT~\slot_name)}
   \ifnum\slot_number=\boundary_char
      \out_lline{(LABEL~BOUNDARYCHAR)}
   \fi
   \let\vpl_liglabel=\relax
   \let\vpl_ligstop=\out_ligstop
}
\let\vpl_liglabel=\out_liglabel
%    \end{macrocode}
%
% |\vpl_ligstop| writes out a |LIGSTOP| instruction if appropriate.
%
%    \begin{macrocode}
\def\out_ligstop{\out_lline{(STOP)}
   \let\vpl_liglabel=\out_liglabel
   \let\vpl_ligstop=\relax}
\let\vpl_ligstop=\relax
%    \end{macrocode}
%
%
% \subsubsection{The characters}
%
%    \begin{macrocode}
\def\make_characters#1{
   \bgroup
      \let\do_slot=\do_character
      \let\end_do_slot=\end_do_character
      \let\nextlarger=\vpl_nextlarger
      \let\varchar=\vpl_varchar
      \let\endvarchar=\end_vpl_varchar
      \let\vartop=\vpl_vartop
      \let\varmid=\vpl_varmid
      \let\varbot=\vpl_varbot
      \let\varrep=\vpl_varrep
      \if_including_map
         \let\saved_raw\vpl_raw
         \let\saved_rule\vpl_rule
         \let\saved_special\vpl_special
         \let\saved_warning\vpl_warning
         \let\saved_movert\vpl_movert
         \let\saved_moveup\vpl_moveup
         \let\saved_push\vpl_push
         \let\saved_pop\vpl_pop
      \else
         \let\do_character_map\relax
      \fi
      \pre_fourth_etx_pass_hook
      \inputetx{#1}
      \post_fourth_etx_pass_hook
   \egroup
}
%    \end{macrocode}
%
% |\do_character| produces a character entry for glyph |\slot_name| in
% slot |\slot_number|.  First of all, the version with letterspacing:
%
%    \begin{macrocode}
\def\do_character{
   \x_cs\ifx{g-\slot_name}\relax
      \expandafter\gobble_setslot
   \else
      \ifx\slot_name\notdef_name\else
         \out_line{(CHARACTER~\vpl_int\slot_number\space
            (COMMENT~\slot_name)}
         \afm_convert\a_dimen=\width\slot_name;
         \do_character_sidebearings
         \out_lline{(CHARWD~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\height\slot_name;
         \out_lline{(CHARHT~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\depth\slot_name;
         \out_lline{(CHARDP~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\italic\slot_name;
         \ifnum\a_dimen>0
            \out_lline{(CHARIC~\vpl_real\a_dimen)}
         \fi
         \do_character_map
      \fi
   \fi
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\do_character_sidebearings{
   \ifisint{\slot_name-spacing}\then
      \afm_convert\curr_bearings=\int{\slot_name-spacing};
      \divide\curr_bearings by 2
   \else
      \curr_bearings=\side_bearings
   \fi
   \afm_convert\a_dimen=\width\slot_name;
   \advance\a_dimen by 2\curr_bearings
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\do_character_map{
   \global\prev_mapfont=0 \out_lline{(MAP}
      \ifdim \curr_bearings=0pt
         \mapcommands\slot_name
      \else
         \out_llline{(MOVERIGHT~\vpl_real\curr_bearings)}
         \mapcommands\slot_name
         \out_llline{(MOVERIGHT~\vpl_real\curr_bearings)}
      \fi
   \out_llline{)}
}
%    \end{macrocode}
%
% Now the version without:
%
%    \begin{macrocode}
\def\do_character_no_letterspacing{
   \x_cs\ifx{g-\slot_name}\relax
      \expandafter\gobble_setslot
   \else
      \ifx\slot_name\notdef_name\else
         \out_line{(CHARACTER~\vpl_int\slot_number\space
            (COMMENT~\slot_name)}
         \afm_convert\a_dimen=\width\slot_name;
         \out_lline{(CHARWD~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\height\slot_name;
         \out_lline{(CHARHT~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\depth\slot_name;
         \out_lline{(CHARDP~\vpl_real\a_dimen)}
         \afm_convert\a_dimen=\italic\slot_name;
         \ifnum\a_dimen>0 \out_lline{(CHARIC~\vpl_real\a_dimen)} \fi
         \global\prev_mapfont=0 \out_lline{(MAP}
            \mapcommands\slot_name
         \out_llline{)}
      \fi
   \fi
}
\def\gobble_setslot#1\endsetslot{\endsetslot}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\end_do_character{
   \ifisglyph\slot_name\then
      \out_lline{)}
   \fi
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\notdef_name{.notdef}
%    \end{macrocode}
%
%
% \subsubsection{Slot commands that end up in a character property list}
%
% |\vpl_nextlarger{NAME}| produces a |NEXTLARGER| entry.
%
%    \begin{macrocode}
\def\vpl_nextlarger#1{
   \ifisint{#1}\then
      \out_lline{(NEXTLARGER~D~\the\int{#1})~(COMMENT~#1)}
   \else
      \immediate\write16{Warning:~\string\nextlarger\space
         for~unknown~slot~`#1'}
   \fi
}
%    \end{macrocode}
%
% |\vpl_varchar VARCHAR COMMANDS \end_vpl_varchar| produces
% a |VARCHAR| entry.
%
%    \begin{macrocode}
\def\vpl_varchar{\out_lline{(VARCHAR}}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\vpl_vartop#1{
   \ifisint{#1}\then
      \out_llline{(TOP~D~\the\int{#1})~(COMMENT~#1)}
   \else
      \immediate\write16{Warning:~\string\vartop\space
         for~unknown~slot~`#1'}
   \fi
}
\def\vpl_varmid#1{
   \ifisint{#1}\then
      \out_llline{(MID~D~\the\int{#1})~(COMMENT~#1)}
   \else
      \immediate\write16{Warning:~\string\varmid\space
         for~unknown~slot~`#1'}
   \fi
}
\def\vpl_varbot#1{
   \ifisint{#1}\then
      \out_llline{(BOT~D~\the\int{#1})~(COMMENT~#1)}
   \else
      \immediate\write16{Warning:~\string\varbot\space
         for~unknown~slot~`#1'}
   \fi
}
\def\vpl_varrep#1{
   \ifisint{#1}\then
      \out_llline{(REP~D~\the\int{#1})~(COMMENT~#1)}
   \else
      \immediate\write16{Warning:~\string\varrep\space
         for~unknown~slot~`#1'}
   \fi
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\end_vpl_varchar{\out_llline{)}}
%    \end{macrocode}
%
%
% \subsubsection{Saved map commands}
%
% The following commands (and |\vpl_scale|, which is defined above) can
% be used in a glyph:
%
%    \begin{macrocode}
\def\vpl_raw#1#2#3{
   \global\next_mapfont=\csname~#1-\the\scaled_design_size\endcsname\relax
   \ifnum\next_mapfont=\prev_mapfont\else
      \out_llline{(SELECTFONT~\vpl_int\next_mapfont)~
         (COMMENT~#1~at~\the\scaled_design_size)}
   \fi
   \out_llline{(SETCHAR~D~#2)~(COMMENT~#3)}
   \global\prev_mapfont=\next_mapfont
}
%    \end{macrocode}
%    \begin{macrocode}
\def\vpl_rule#1#2{
   \afm_convert\a_dimen=#1;
   \afm_convert\b_dimen=#2;
   \out_llline{(SETRULE~\vpl_real\b_dimen\space~\vpl_real\a_dimen)}
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\vpl_special#1{
   \out_llline{(SPECIAL~#1)}}
\def\vpl_warning#1{
   \out_llline{(SPECIAL~Warning:~#1)}
   \immediate\write16{Warning:~#1.}
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\vpl_movert#1{
   \afm_convert\a_dimen=#1;
   \out_llline{(MOVERIGHT~\vpl_real\a_dimen)}
}
\def\vpl_moveup#1{
   \afm_convert\a_dimen=#1;
   \out_llline{(MOVEUP~\vpl_real\a_dimen)}
}
%    \end{macrocode}
%
%    \begin{macrocode}
\def\vpl_push{\out_llline{(PUSH)}}
\def\vpl_pop{\out_llline{(POP)}}
%    \end{macrocode}
%
%
% \subsubsection{Tidying up}
%
%    \begin{macrocode}
\def\make_tidy#1{
   \tidying_up_hook
   \if_including_map
      \a_count=0\loop\ifnum\a_count<\font_count
         \edef\temp_command{\csname~f-\the\a_count\endcsname}
         \global\x_cs\let\temp_command\relax
         \advance\a_count by 1
      \repeat
      \global\font_count=0
   \fi
}
%    \end{macrocode}
%
%
% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


Finally, there is one other thing I would like to see included in fontinst,
now that there is an update coming anyway. It is my idea of "metric command
packages", which could be included as the following subsection, presumably
of section 10:

% \subsection{Metric packages}
%
% The idea of the metric package commands is to make it easier to
% divide the large chunk of commands that is normally kept in |latin.mtx|
% into smaller parts kept in several files, which is handy if one wants
% to make minor modifications to some of the commands or simply leave some
% of the parts in |latin.mtx| out to speed up the fontmaking\footnote{In
% my experience, the speedup can be considerable.}. They are not of any
% use to those who use fontinst only at the |\latinfamily| level, but
% they should prove useful to those who are in the business of modifying
% |latin.mtx| or writing their own alternatives, as it simplifies
% modularisation.
%
% The main problem with splitting up |latin.mtx| (or some other metric
% file which fulfills an equivalent function) is that there are some
% commands which are defined at the top and which are then used in almost
% all sections of the file. One must make certain that the these commands
% are always defined, which makes the metric files somewhat harder to
% use (especially if the one who tries to use them is not the one who
% wrote them).
%
% One strategy is to include all definitions needed for a metric file in
% it. This has the disadvantage that the commands will have to be
% definied several times. Furthermore the commands will appear in
% several files, so if one finds a bug in one of them, one will have to
% correct this bug in several files (the number of files can soon become
% quite large), a boring procedure indeed.
%
% Another strategy is to put all the command definitions in one file
% and then explicitly include it in the \meta{file-list} argument of
% |\installfont|. This eliminates the repeated bug fixing problem, but
% requires the user to do something that the computer can actually do
% just as well.
%
% A third strategy is to put the command definitions in one or several
% files and then in each metric file the user explicitly menions load
% the command definitions needed for that particular file. Metric
% packages uses an improved version of this strategy, since they also
% make it possible for fontinst to remember which packages (i.e., sets
% of command definitions) that have already been loaded, so that they
% are not unnecessarily loaded again.
%
% The names of the commands have been chosen so that they resemble the
% names of similar commands in \LaTeXe, only a |mtx| has been added to
% the command names: To load a package, the calling metric file executes
% the |\usemtxpackage| command. A package signals that it has been
% loaded through the |\ProvidesMtxPackage|. The package mechanism is
% simpler than that of \LaTeXe\ though, there is no option mechanism and
% packages do not announce to the user that they are being used (perhaps
% they should, but that can always be added later).
%
% \begin{macro}{\ProvideMtxPackage}
%   The call
%   \begin{quote}
%     |\ProvideMtxPackage{|\meta{package~name}|}|
%   \end{quote}
%   signals to the package managing system that there is no need to load
%   this package again.
%    \begin{macrocode}
\def\ProvidesMtxPackage#1{\x_cs\let{pack-#1}P}
%    \end{macrocode}
% \end{macro}
%
%
% \begin{macro}{\usemtxpackage}
%   The call
%   \begin{quote}
%     |\usemtxpackage{|\meta{package~list}|}|
%   \end{quote}
%   inputs those of the packages in the list that have not been loaded
%   yet (i.e., those for which no |\ProvideMtxPackage| has been made).
%   Each package is assumed to reside in the metric file that
%   |\inputmtx| loads when given the name of the package as argument.
%
%   The call
%   \begin{quote}
%     |\usemtxpackage[|\meta{filename}|]{|\meta{package~list}|}|
%   \end{quote}
%   inputs the packages in the list if at least one of them has not been
%   loaded yet. In this case, all the packages are assumed to reside in
%   the single metric file that |\inputmtx| loads when given
%   \meta{filename} as argument.
%
%    \begin{macrocode}
\def\usemtxpackage{\futurelet\next_token\test_UseMtxPkg_arguments}
\def\test_UseMtxPkg_arguments{\ifx\next_token[
      \expandafter\mtx_package_given_file
   \else
      \expandafter\mtx_package_separate_files
   \fi
}
%    \end{macrocode}
%    \begin{macrocode}
\newif\if_load_mtx_package_
\def\mtx_package_given_file[#1]#2{
   \_load_mtx_package_false
   \process_csep_list\load_true_unless_loaded #2,\process_csep_list,
   \if_load_mtx_package_ \inputmtx{#1} \fi
}
\def\load_true_unless_loaded#1{
   \x_cs\ifx{pack-#1}P\else\_load_mtx_package_true\fi
}
%    \end{macrocode}
%    \begin{macrocode}
\def\mtx_package_separate_files#1{
   \process_csep_list\load_file_unless_loaded #1,\process_csep_list,
}
\def\load_file_unless_loaded#1{
   \x_cs\ifx{pack-#1}P\else \inputmtx{#1} \fi
}
%    \end{macrocode}
% \end{macro}
%
%

The long general comment in the beginning probably shouldn't go into the
final fontinst.dtx, but I though it might be useful for you now.

I have not made any changes to fontdoc.


Lars Hellström