虚假空间

虚假空间

我正在尝试编写一个\spaced宏:它以字符串作为参数,分解该字符串,然后根据给定的 重新组合它,将每个标记与其他标记分开\spreadratio。以下是宏以及 MWE:

\catcode`\@=11

\font\sc="Latin Modern Roman Caps:mapping=tex-text,+onum"

\chardef\box@temp@la=0
\dimendef\dimen@temp@la=0
\dimendef\dimen@temp@lb=2
\toksdef\toks@temp@ga=1

% \boxit is only aimed to highlight the problem.
% If you wanna see the MWE without boxing just comment this
% definition and uncomment the following one.
\def\boxit#1{\vbox{\hrule\hbox{\vrule#1\vrule}\hrule}}
%\def\boxit#1{#1}

\newcount\spreadratio \spreadratio=100

% The interface of \spaced is the following:
%
%    \spaced[<number>]{<token list>}
%
% where the optional parameter is the amount of \spreadratio
% other than the predefined one.
\def\spaced{\futurelet\firsttok\@spaced}

\def\@spaced{%
  \if\firsttok[\let\next\@paramspaced
  \else\let\next\@simplespaced\fi
  \next}

\def\@paramspaced[#1]#2{{\spreadratio=#1\dosp@ced{#2}}}

\def\@simplespaced#1{\dosp@ced{#1}}

\def\dosp@ced#1{{\setbox\box@temp@la\hbox{#1}%
  % the size of the \hbox is changed according
  % to the value of \spreadratio
  \dimen@temp@la\wd\box@temp@la \dimen@temp@lb\dimen@temp@la
  \divide\dimen@temp@lb by\@m
  \multiply\dimen@temp@lb by\spreadratio
  \advance\dimen@temp@la by\dimen@temp@lb
  \global\toks@temp@ga={}%
  \@split{#1}%
  \setbox\box@temp@la\hbox to\dimen@temp@la{\the\toks@temp@ga}%
  \box\box@temp@la}}

\def\@split#1{\@@split#1\end}

\def\@@split#1{%
  \ifx#1\end
    \global\toks@temp@ga=\expandafter{\the\toks@temp@ga\unskip}%
    \let\next\relax
  \else
    \global\toks@temp@ga=\expandafter{\the\toks@temp@ga#1\hfil}%
    \let\next\@@split\fi
  \next}

\catcode`\@=12

\noindent
\boxit{The music}\par\noindent
\boxit{\spaced{The music of Steve Reich}}\par\noindent
\boxit{\spaced[300]{The music of Steve Reich}}\par\noindent
\boxit{{\sc The music of Steve Reich}}\par\noindent
\boxit{\spaced{\sc The music of Steve Reich}}\par\noindent
\boxit{\spaced[300]{\sc The music of Steve Reich}}\par\noindent
% Note that here are no spaces between '\it' and the word 'The'.
\boxit{{\it{}The music of Steve Reich}}\par\noindent
\boxit{\spaced{\it{}The music of Steve Reich}}\par\noindent
\boxit{\spaced[300]{\it{}The music of Steve Reich}}\par\noindent
\boxit{\spaced{The music of Steve Reich}}\par\noindent
\end

结果如下。 在此处输入图片描述

如您所见(注意单词 'the' 之前的空格),当强制参数的第一个标记是宏(就像\scor 一样\it)时,\spaced会在第二个标记之前添加一种虚假空格。

我无法想象那个空间从何而来,而且我也无法消除它。

我的错在哪里?

答案1

你的拆分例程不区分字母和命令,因此如果你尝试存储在中\spaced{\sc A b}的最终标记列表\toks@temp@ga

\sc \hfil A\hfil b\hfil \unskip

\showthe\toks@temp@ga正如您在之后添加到宏中看到的\let\next\relax

当然,分裂程序也会占用空间。

不同的策略是使用 XeTeX 功能。

\catcode`\@=11

\font\rm="Latin Modern Roman:mapping=tex-text,+onum" \rm
\font\sc="Latin Modern Roman Caps:mapping=tex-text,+onum"

\def\spaced{\futurelet\next\@spaced}
\def\@spaced{%
  \ifx\next[%
    \expandafter\@spacedopt
  \else
    \expandafter\@spacednonopt
  \fi}
\def\@spacednonopt#1{#1}
\def\@spacedopt[#1]#2{\begingroup\@addtofont{letterspace=#1}#2\endgroup}
\def\@getfontname{\expandafter\@removequotes\fontname\font}
\def\@removequotes"#1"{#1}
\def\@addtofont#1{%
  \begingroup\edef\x{\endgroup
    \font\noexpand\@temp="\expandafter\@removequotes\fontname\font;#1;"%
    \noexpand\@temp}\x}
\catcode`@=12

\spaced[10]{The music of Bach} This is not spaced

\sc\spaced[10]{The music of Bach} This is not spaced
\bye

在此处输入图片描述

答案2

@egreg 说:

你的拆分例程不区分字母和命令,因此如果你尝试存储在中\spaced{\sc A b}的最终标记列表\toks@temp@ga

\sc \hfil A\hfil b\hfil \unskip

\showthe\toks@temp@ga正如您在之后添加到宏中看到的\let\next\relax

好吧,根据这个解释(以及我对宏参数化的担忧),我按照以下方式更改了拆分例程

\def\@@split#1{%
  \ifx#1\end
    \global\toks@temp@ga=\expandafter{\the\toks@temp@ga\unskip}%
    \let\next\relax
  \else
    % here the routine should dinstiguish
    % between letters and commands
    %
    \ifcat\noexpand#1\relax
      \global\toks@temp@ga=\expandafter{\the\toks@temp@ga#1}%
      \let\next\@@split
    %
    % %%%
    \else
      \global\toks@temp@ga=\expandafter{\the\toks@temp@ga#1\hfil}%
      \let\next\@@split\fi\fi
  \next}

它似乎工作正常,但如果 egreg 没有建议这个系统肯定有问题

这仅仅是一种不安全感,还是代码中隐藏着一个不易察觉的错误?

相关内容