如何正确编写返回尺寸的命令/宏

如何正确编写返回尺寸的命令/宏

我正在尝试编写一个命令/宏,它将返回一个可用于配置我所创建的环境的维度。

例如,我有一个TwoColumn环境,其中我想包含 4.5pt 的垂直偏移:

\begin{TwoColumns}{4.5pt}{chant}
    ... Content ...
\end{TwoColumns}

无需维护固定尺寸列表,只需几个参数即可生成精确的垂直间距。我编写了一个命令,它使用参数来计算感兴趣的尺寸。调用以下命令通过它自己根据控制台输出正确计算了 4.5pt 尺寸:

\GetTopMarginAdjust{la}{c3}{sameLine}

但是,当使用这个新命令作为的参数时\vspace,它会引发一个错误:缺失数字,视为零TwoColumn. 与我的环境类似。

\vspace{\GetTopMarginAdjust{la}{c3}{sameLine}}

我遗漏了什么?我该如何以正确或更好的方式做到这一点?

\documentclass[twoside,12pt]{book}
\usepackage{xifthen}

% Heading to top staff line c3/f3 clef
%    No divisio minima, no Mi note, no descenders in ChantBoxCentered
\newdimen\vspaceAbsCThreeCB
\vspaceAbsCThreeCB=10.5pt

% Heading to top staff line c4 clef
%    No divisio minima, no descenders in ChantBoxCentered
\newdimen\vspaceAbsCFourCB
\vspaceAbsCFourCB=4pt

% Delta between ChantBoxCentered (English) and TwoColumn (Latin) environments
\newdimen\vspaceDeltaEnv
\vspaceDeltaEnv=6pt

% Delta between twoLine and sameLine (absolute difference)
\newdimen\vspaceDeltaLineOption
\vspaceDeltaLineOption=0.5pt

\newcommand{\tempValueLanguageAndClef}{0pt}
\newcommand{\tempValueLineOption}{0pt}

%
% GetTopMarginAdjust
%    Argument #1: (str) language (la or en)
%    Argument #2: (str) clef (c3 or c4)
%    Argument #3: (str) lineOption (sameLine or twoLine)
%
\newcommand{\GetTopMarginAdjust}[3]{
    % Set basic environment: Get the language and clef
    \ifthenelse{\equal{#1}{en}}{%
        % ChantBoxCentered (English)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \renewcommand{\tempValueLanguageAndClef}{\vspaceAbsCThreeCB}
        }{%
            % c4 clef
            \renewcommand{\tempValueLanguageAndClef}{\vspaceAbsCFourCB}
        }
    }{%
        % TwoColumn (Latin)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \renewcommand{\tempValueLanguageAndClef}{\dimexpr \vspaceAbsCThreeCB-\vspaceDeltaEnv\relax}
        }{%
            % c4 clef
            \renewcommand{\tempValueLanguageAndClef}{\dimexpr \vspaceAbsCFourCB-\vspaceDeltaEnv\relax}
        }
    }%
    
    % Set delta needed for the line option
    \ifthenelse{\equal{#3}{sameLine}}{%
        \renewcommand{\tempValueLineOption}{0pt}
    }{%
        \renewcommand{\tempValueLineOption}{\vspaceDeltaLineOption}
    }
    
    \typeout{dim: \the\dimexpr \tempValueLanguageAndClef+\tempValueLineOption\relax; Options: #1, #2, #3}
    \the\dimexpr \tempValueLanguageAndClef+\tempValueLineOption\relax
}

\begin{document}

\GetTopMarginAdjust{en}{c3}{sameLine}
\GetTopMarginAdjust{la}{c3}{sameLine}

% Doesn't work
\vspace{\GetTopMarginAdjust{la}{c3}{sameLine}}

% Doesn't work
\newdimen\tempOffset
\tempOffset=\GetTopMarginAdjust{la}{c3}{sameLine}

\typeout{\tempOffset}

\end{document}

答案1

您的问题是,对于您想要使用的方式,\GetTopMarginAdjust它必须是完全可扩展的,但是定义的条件xifthen和您为计算所做的分配并不是完全可扩展的。

下面使用\str_case:nnFexpl3获得一些完全可扩展的基于字符串的分支。

\documentclass[twoside,12pt]{book}

% Heading to top staff line c3/f3 clef
%    No divisio minima, no Mi note, no descenders in ChantBoxCentered
\newdimen\vspaceAbsCThreeCB
\vspaceAbsCThreeCB=10.5pt

% Heading to top staff line c4 clef
%    No divisio minima, no descenders in ChantBoxCentered
\newdimen\vspaceAbsCFourCB
\vspaceAbsCFourCB=4pt

% Delta between ChantBoxCentered (English) and TwoColumn (Latin) environments
\newdimen\vspaceDeltaEnv
\vspaceDeltaEnv=6pt

% Delta between twoLine and sameLine (absolute difference)
\newdimen\vspaceDeltaLineOption
\vspaceDeltaLineOption=0.5pt

\ExplSyntaxOn
\msg_new:nnn { fong } { unknown-language } { Unknown~ language~ `#1'. }
\msg_new:nnn { fong } { unknown-clef } { Unknown~ clef~ `#1'. }
\msg_new:nnn { fong } { unknown-lineoption } { Unknown~ lineoption~ `#1'. }
\cs_new:Npn \fong_topmargin_adjust:nnn #1#2#3
  {
    \dim_eval:n
      {
        \str_case:nnF {#2}
          {
            { c3 } { \vspaceAbsCThreeCB }
            { c4 } { \vspaceAbsCFourCB }
          }
          {
            \msg_expandable_error:nnn { fong } { unknown-clef } {#2}
            \vspaceAbsCThreeCB % fallback value
          }
        \str_case:nnF {#1}
          {
            { en } { }
            { la } { - \vspaceDeltaEnv }
          }
          { \msg_expandable_error:nnn { fong } { unknown-language } {#1} }
        \str_case:nnF {#3}
          {
            { sameLine } { }
            { twoLine  } { + \vspaceDeltaLineOption }
          }
          { \msg_expandable_error:nnn { fong } { unknown-lineoption } {#3} }
      }
  }
\cs_new_eq:NN \GetTopMarginAdjust \fong_topmargin_adjust:nnn
\ExplSyntaxOff

\begin{document}

\GetTopMarginAdjust{en}{c3}{sameLine}
\GetTopMarginAdjust{la}{c3}{sameLine}

% Works
\vspace{\GetTopMarginAdjust{la}{c3}{sameLine}}

% Works
\newdimen\tempOffset
\tempOffset=\GetTopMarginAdjust{la}{c3}{sameLine}

\typeout{\the\tempOffset}

\end{document}

答案2

欢迎来到 TeX.SE。\vpsace需要一些东西扩展到⟨glue⟩,但是你的\GetTopMarginAdjust宏不可扩展。

解决这个问题的一种方法是让这个宏将其结果存储在 dimen 或 skip 寄存器中(后者是 LaTeX 使用的寄存器\newlength;与 dimen 寄存器相反,skip 寄存器有收缩和拉伸组件);然后该寄存器可以在的参数中使用它\vspace

\documentclass{article}
\usepackage{xifthen}

% Heading to top staff line c3/f3 clef
%    No divisio minima, no Mi note, no descenders in ChantBoxCentered
\newcommand{\vspaceAbsCThreeCB}{10.5pt}

% Heading to top staff line c4 clef
%    No divisio minima, no descenders in ChantBoxCentered
\newcommand{\vspaceAbsCFourCB}{4pt}

% Delta between ChantBoxCentered (English) and TwoColumn (Latin) environments
\newcommand{\vspaceDeltaEnv}{6pt}

% Delta between twoLine and sameLine (absolute difference)
\newcommand{\vspaceDeltaLineOption}{0.5pt}

\newcommand{\tempValueLanguageAndClef}{}
\newcommand{\tempValueLineOption}{}

\newdimen\topMarginAdjustResult % where \topMarginAdjust stores its result

% \topMarginAdjust
%    Argument #1: (str) language (la or en)
%    Argument #2: (str) clef (c3 or c4)
%    Argument #3: (str) lineOption (sameLine or twoLine)
%
\newcommand*{\topMarginAdjust}[3]{%
    % Set basic environment: Get the language and clef
    \ifthenelse{\equal{#1}{en}}{%
        % ChantBoxCentered (English)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \let\tempValueLanguageAndClef\vspaceAbsCThreeCB
        }{%
            % c4 clef
            \let\tempValueLanguageAndClef\vspaceAbsCFourCB
        }%
    }{%
        % TwoColumn (Latin)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \edef\tempValueLanguageAndClef{%
                \the\dimexpr \vspaceAbsCThreeCB-\vspaceDeltaEnv\relax}%
        }{%
            % c4 clef
            \edef\tempValueLanguageAndClef{%
              \the\dimexpr \vspaceAbsCFourCB-\vspaceDeltaEnv\relax}%
        }%
    }%
    %
    % Set delta needed for the line option
    \ifthenelse{\equal{#3}{sameLine}}{%
        \renewcommand{\tempValueLineOption}{0pt}%
    }{%
        \edef\tempValueLineOption{\vspaceDeltaLineOption}
    }%
    % This because \topMarginAdjustResult is a dimen register; if it were a
    % macro, you would use \edef and \the\dimexpr ... \relax inside.
    \topMarginAdjustResult=%
      \dimexpr \tempValueLanguageAndClef+\tempValueLineOption\relax
}

\begin{document}

Foo\par
\topMarginAdjust{la}{c3}{sameLine}%
\vspace{\topMarginAdjustResult}

Bar\par
\topMarginAdjust{en}{c3}{sameLine}%
\vspace{\topMarginAdjustResult}

Baz

\newdimen\tempOffset
\topMarginAdjust{la}{c3}{sameLine}%
\tempOffset=\topMarginAdjustResult

\typeout{\the\tempOffset} % 4.5pt

\end{document}

在此处输入图片描述

另一种方法是将结果存储在宏中:您可以拥有每次重复使用的单个宏,或者添加一个参数来\topMarginAdjust指定用于存储计算结果的宏名称。

\documentclass{article}
\usepackage{xifthen}

% Heading to top staff line c3/f3 clef
%    No divisio minima, no Mi note, no descenders in ChantBoxCentered
\newcommand{\vspaceAbsCThreeCB}{10.5pt}

% Heading to top staff line c4 clef
%    No divisio minima, no descenders in ChantBoxCentered
\newcommand{\vspaceAbsCFourCB}{4pt}

% Delta between ChantBoxCentered (English) and TwoColumn (Latin) environments
\newcommand{\vspaceDeltaEnv}{6pt}

% Delta between twoLine and sameLine (absolute difference)
\newcommand{\vspaceDeltaLineOption}{0.5pt}

\newcommand{\tempValueLanguageAndClef}{}
\newcommand{\tempValueLineOption}{}

\newcommand{\topMarginAdjustResult}{}% where \topMarginAdjust stores its result

% \topMarginAdjust
%    Argument #1: (str) language (la or en)
%    Argument #2: (str) clef (c3 or c4)
%    Argument #3: (str) lineOption (sameLine or twoLine)
%
\newcommand*{\topMarginAdjust}[3]{%
    % Set basic environment: Get the language and clef
    \ifthenelse{\equal{#1}{en}}{%
        % ChantBoxCentered (English)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \let\tempValueLanguageAndClef\vspaceAbsCThreeCB
        }{%
            % c4 clef
            \let\tempValueLanguageAndClef\vspaceAbsCFourCB
        }%
    }{%
        % TwoColumn (Latin)
        \ifthenelse{\equal{#2}{c3}}{%
            % c3 clef
            \edef\tempValueLanguageAndClef{%
                \the\dimexpr \vspaceAbsCThreeCB-\vspaceDeltaEnv\relax}%
        }{%
            % c4 clef
            \edef\tempValueLanguageAndClef{%
              \the\dimexpr \vspaceAbsCFourCB-\vspaceDeltaEnv\relax}%
        }%
    }%
    %
    % Set delta needed for the line option
    \ifthenelse{\equal{#3}{sameLine}}{%
        \renewcommand{\tempValueLineOption}{0pt}%
    }{%
        \edef\tempValueLineOption{\vspaceDeltaLineOption}
    }%
    %
    \edef\topMarginAdjustResult{%
      \the\dimexpr \tempValueLanguageAndClef+\tempValueLineOption\relax
    }%
}

\begin{document}

Foo\par
\topMarginAdjust{la}{c3}{sameLine}%
\vspace{\topMarginAdjustResult}

Bar\par
\topMarginAdjust{en}{c3}{sameLine}%
\vspace{\topMarginAdjustResult}

Baz

% TeX way:
\newdimen\tempOffset
\topMarginAdjust{la}{c3}{sameLine}%
\tempOffset=\topMarginAdjustResult\relax % don't remove the \relax

% LaTeX way:
% \newlength{\tempOffset}%
% \topMarginAdjust{la}{c3}{sameLine}%
% \setlength{\tempOffset}{\topMarginAdjustResult}%

\typeout{\the\tempOffset} % 4.5pt

\end{document}

与上面相同的输出。

相关内容