我正在尝试编写一个命令/宏,它将返回一个可用于配置我所创建的环境的维度。
例如,我有一个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:nnF
来expl3
获得一些完全可扩展的基于字符串的分支。
\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}
与上面相同的输出。