我正在尝试制作一个接受可变数量文本变量的签名块宏,这样我就可以添加或减去文本而不使用空字段“{}”作为参数。
\sigblock{0}{2}{Notary Public}
生成:
和,
\sigblock{0}{3}{Notary Public}{At Large}
生成:
在宏中,前两个变量是线的水平位置和线的长度,其余变量是文本。
我尝试过这个:
\documentclass{article}
\usepackage{ifthen}
\def\sigblock#1#2#3#4% Defined 4 arguments%
{\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
\par\noindent\hskip#1in#3\par\noindent\hskip#1in#4}}
\def\sigblockB#1#2#3% Defined 3 arguments%
{\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
\par\noindent\hskip#1in#3}}
\ifthenelse{\equal{%
\sigblock}{#1}{#2}{#3}}%
{\renewcommand{\sigblockB}{%
\sigblock%
}}
\begin{document}
\sigblock{0}{3}{Notary Public}{At Large}
\sigblock{0}{3}{Notary Public}
\sigblockB{0}{3}{Notary Public}
\end{document}
使用 lualatex 编译时生成:
日志文件中还有大量错误。在我的 MWE 中,我尝试使用替换\renewcommand
。我确实想到可能有一个涉及 的解决方案。一个可以“扩展”以允许更多文本行的解决方案是最好的。\sigblock
\sigblockB
\let
当我填写签名栏时,我经常需要剪切和粘贴信息。我个人认为,一开始使用一系列括号更容易做到这一点,之后对校对也很有帮助。
我意识到我的问题反映了我对变量工作原理的无知。如果能解释一下我的尝试从根本上出了什么问题,并给出解决方案,我将不胜感激。
有关的:创建右对齐签名块
答案1
虽然我建议使用@egreg的答案,但这里有一种方法可以实现你正在寻找的使用\@ifnextchar
。这个想法是\sigblock
准备好一切,直到处理签名字段下方的行。然后\sigblock@
调用它将下一个分组参数设置为签名字段下方的一行并开始递归。当没有剩余参数时,递归终止:
\def\sigblock@#1#2{%
\par\hskip#1in#2
\@ifnextchar\bgroup{\sigblock@{#1}}\endgroup
}
\documentclass{article}
\usepackage{lipsum}
\usepackage[doublespacing]{setspace}%doublespacing is active for testing purposes
\makeatletter
\def\sigblock#1#2#3{%
\begingroup
\singlespacing
\parindent\z@
\vskip.75in
\vbox{%
\hskip#1in%
\hbox to #2in{%
\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill
}
}%
\sigblock@{#1}{#3}
}
\def\sigblock@#1#2{%
\par\hskip#1in#2\par
\@ifnextchar\bgroup{\sigblock@{#1}}\endgroup
}
\makeatother
\begin{document}
\sigblock{0}{3}{Notary Public}{At Large}{At Large}{At Large}{At Large}{At Large}
\lipsum
\end{document}
笔记。由于额外的行是可选的,您可能希望\sigblock{0}{3}{Notary Public}[At Large]...
遵循标准接口设计,用括号表示可选参数。在这种情况下,您可以定义:
\def\sigblock#1#2#3{%
\begingroup
\singlespacing
\parindent\z@
\vskip.75in
\vbox{%
\hskip#1in%
\hbox to #2in{%
\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill
}
}%
\sigblock@{#1}[#3]
}
\def\sigblock@#1[#2]{%
\par\hskip#1in#2\par
\@ifnextchar[{\sigblock@{#1}}\endgroup
}
答案2
您想太多了:工具已经在那里了,即tabular
。
\documentclass{article}
\newcommand{\sigblock}[3]{%
\par\vspace{\medskipamount}\noindent
\hspace*{#1in}\makebox[#2in]{\hrulefill}\\*[.2ex]
\hspace*{#1in}%
\begin{tabular}{@{}l@{}}
#3
\end{tabular}%
}
\begin{document}
\sigblock{0}{3}{Notary Public \\ At Large}
\sigblock{0}{3}{Notary Public}
\sigblock{1}{3}{Notary Public \\ At Large \\ Again \\ What else?}
\end{document}
如果你需要添加\singlespacing
,请记住不是带有参数的命令,但带有声明。因此你应该
\newcommand{\sigblock}[3]{%
\par{\singlespacing\vspace{\medskipamount}\noindent
\hspace*{#1in}\makebox[#2in]{\hrulefill}\\*[.2ex]
\hspace*{#1in}%
\begin{tabular}{@{}l@{}}
#3
\end{tabular}%
\par}%
}
答案3
在尝试解决这个问题的过程中,我想到了一个可能很有趣的宏,所以我想在这里分享它。这个宏被调用\vardef
,并允许我们定义一个宏,它可以接收用大括号括起来的可变数量的参数(任意数量,不限于 9 个参数)。
在内联宏的定义中,\vardef
可以使用宏\NUMARGS
来检索宏在“执行”时接收的参数数量;并且可以使用宏、、、\ARG1
...来检索宏的参数(如果存在)。\ARG2
\ARG3
例如,如果将\vardef
宏定义\mymacro
如下,
\newcount\tmpnum
\vardef\mymacro{This macro is being used with \NUMARGS{} arguments.
They are: \ifnum\NUMARGS>0 \tmpnum=1 \loop \ARG{\the\tmpnum}
\ifnum\NUMARGS>\tmpnum \advance\tmpnum by 1 \repeat\fi}
然后“执行”\mymacro{first}{second}{third}
给出“输出”
This macro is being used with 3 arguments. They are: first second third
\mymacro{first}{second}{third}{fourth}{fifth}
并给予“执行”
This macro is being used with 5 arguments. They are: first second third fourth fifth
\vardef
定义方式如下:
% Firstly we create a new counter to store the number
% of the current argument that is being read.
% Arguments start from 1, so we initially set it to 1.
\newcount\vardefnum
\vardefnum=1
% \vardef receives two arguments: the first is the name of
% the macro to be \vardef-ined and the second is its definition.
\def\vardef#1#2{%
% We momentaneously store the definition in a macro called
% \vardefinition for later use.
\def\vardefinition{#2}
% Now we actually define our control sequence. We define it
% basically to be \grab (a macro whose purpose is explained below).
\def#1{%
% Before actually grabbing arguments with \grab, we give
% an empty useless definition to \invardef so that we will be able
% later to know that we are in the defining code of a
% \vardef-ined macro (we will see why later).
\def\invardef{}%
\grab}}
% The purpose of \grab is to collect and store all the arguments
% that follow him and to finally actually "execute" the code we
% previously stored in \vardefinition.
% To do that, \grab starts by saving into \next the first token
% after him. Then he passes the job to his sister \grabA.
\def\grab{\futurelet\next\grabA}
% \grabA checks if the token stored in \next is an opening curly
% brackets (or, more properly, a \bgroup). If so, she invokes
% the macro \storeargs. Otherwise she defines the number of
% arguments \numvarargs collected by now to be one less than
% \vardefnum; then she expands \vardefinition and removes
% the traces of her job by calling \clearvardef.
\def\grabA{\ifx\next\bgroup\storearg\else\advance\vardefnum by-1
\edef\numvarargs{\the\vardefnum}\vardefinition\clearvardef\fi}
% \clearvardef undefines \invardef (because now we are exiting
% the argument of the \vardef-ined macro) and restores the
% \vardefnum counter.
\def\clearvardef{\let\invardef\undefined \vardefnum=1\relax}
% \storearg, when is invoked by \grabA, finds on its way the
% sequence \else\advance\vardefnum ... \clearvardef\fi. He wants
% to get rid of it to be able to see what's behind it: so he
% eats all of it using his first argument (the argument #1).
% Now \storearg sees after him an argument enclosed in curly
% brackets. So he puts it in his second argument (the argument #2).
% Next, to make TeX happy, \storearg has to regurgitate
% the \fi he has previously eaten up. After that he defines a new
% control sequence that stores his second argument he has just
% collected. The name of this control sequence starts with
% 'vararg' and ends with the number present in \vardefnum at that
% moment. Finally \storearg increments \vardefnum and takes a rest
% by giving the job back to \grab.
\def\storearg#1\fi#2{\fi
\expandafter\def\csname vararg\the\vardefnum\endcsname{#2}%
\advance\vardefnum by 1\grab}
% The following two macros, \ARG and \NUMARGS, are the user
% interface for actually using inside the \vardef-inition the
% (variable number of) arguments. Ckecking if \invardef is
% defined, they can know if they are called inside the
% \vardef-inition of a macro, and they can throw an error message
% otherwise. What they do is quite self explanatory.
\def\ARG#1{\ifdefined\invardef\else\errmessage{%
\string\ARG\space has meaning only inside a
\string\vardef-ined macro.}\fi
\ifnum#1<1\errmessage{The argument of \string\ARG\space
must be greater than zero!}\fi
\ifnum#1>\numvarargs\errmessage{\string\ARG#1 does not exist!}\fi
\csname vararg#1\endcsname}
\def\NUMARGS{\ifdefined\invardef\numvarargs\else\errmessage{%
\string\NUMARGS\space has meaning only inside a
\string\vardef-ined macro.}\fi}
那么,所提问题的解决方案非常简单:使用\ARG1
、、、... 而不是、、、... 并使用来检查已传递给了多少个参数。\ARG2
\ARG3
#1
#2
#3
\NUMARGS
\sigblock
\documentclass{article}
\usepackage{setspace}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% here goes the above definition of \vardef %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\vardef\sigblock{\singlespacing
\vbox{\vskip.75in\noindent\hskip\ARG1in
{\hbox to \ARG2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}\endgraf
\noindent\hskip\ARG1in\ARG3
\ifnum\NUMARGS=4\endgraf\noindent\hskip\ARG1 in\ARG4\fi}}
\begin{document}
\sigblock{0}{3}{Notary Public}{At Large}
\sigblock{0}{3}{Notary Public}
\end{document}
现在可以非常轻松地使用任意数量的参数\sigblock
。
注意:与普通宏不同,在\vardef
ined 宏中,所有参数都必须括在花括号中,并且参数的右括号和下一个参数的左括号之间不能有空格。
答案4
用一个\Longstack
。
\documentclass{article}
\documentclass{article}
\usepackage{stackengine,lipsum,setspace}
\setstackEOL{\cr}
\def\sigblock#1#2#3% Defined 3 arguments%
{\singlespacing{\vbox{\vskip.75in\noindent\hskip#1in%
{\hbox to #2in{\leaders\hbox to 0.00625in{\hfil.\hfil}\hfill}}}%
\par\noindent\hskip#1in\Longstack[l]{#3}}}
\parindent 0pt
\begin{document}
\lipsum[1]
\sigblock{1}{2}{Notary Public\cr at Large}
\sigblock{0}{2.5}{Notary Public\cr at Large\cr State of Disorder}
\end{document}