我想定义一个命令,以一个句子、一个单词或几个单词作为输入,并将每个单词的首字母加粗。我主要需要这个来缩写,到目前为止,我都是这样手动完成的:
\documentclass[a4paper]{article}
\begin{document}
GNU is a recursive acronym for ``\emph{\bf{G}\normalfont{nu's} \bf{N}\normalfont{ot} \bf{U}\normalfont{nix}}'', chosen because GNU's design is Unix-like, but differs from Unix by being free software and containing no Unix code.
\end{document}
我想要做的是定义一个\BoldAbbrv
可以完成该工作的命令。换句话说:
\documentclass[a4paper]{article}
%\newcommand{\BoldAbbrv}...IMPLEMENTATION
\begin{document}
GNU is a recursive acronym for ``\BoldAbbrv{Gnu's Not Unix}'', chosen because GNU's design is Unix-like, but differs from Unix by being free software and containing no Unix code.
\end{document}
我怎样才能做到这一点?而且在思考这个问题的时候,我想到了一个相关的问题,是否存在这样的概念通配符在乳胶中(例如“*”作为零个或多个字符,“_”作为单个字符)?
答案1
这是一个使用的想法expl3
:
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage{expl3,xparse}
% turn expl3 space on: `:' and `_' are letters now and spaces
% are ignored. To insert a space use `~'.
\ExplSyntaxOn
% declare a new sequence variable:
\seq_new:N \l_pouya_boldfirst_seq
% the internal command:
\cs_new:Npn \pouya_boldfirst:n #1
{
% split the input at every space and put the items in the sequence:
\seq_set_split:Nnn \l_pouya_boldfirst_seq { ~ } { #1 }
% map over every item of the sequence; each item is referred to with
% `##1' as we're inside of a command definition:
\seq_map_inline:Nn \l_pouya_boldfirst_seq
{
% use the head of the item in the argument of \textbf
% and put in the tail of the item after it
% followed by a space:
\textbf { \tl_head:n { ##1 } } \tl_tail:n { ##1 } ~
}
% undo the last space:
\unskip
}
% the document command:
\NewDocumentCommand\BoldFirst{m}
{ \pouya_boldfirst:n { #1 } }
% turn expl3 space off again:
\ExplSyntaxOff
\begin{document}
GNU is a recursive acronym for ``\BoldFirst{Gnu's Not Unix}'', chosen because GNU's
design is Unix-like, but differs from Unix by being free software and containing
no Unix code.
\end{document}
答案2
这似乎是 LaTeX3 的 RegEx 功能的良好应用:
\documentclass{article}
\usepackage{xparse,l3regex}
\ExplSyntaxOn
\NewDocumentCommand{\BoldFirst}{ m }
{
\pouya_boldfirst:n { #1 }
}
\cs_new_protected:Npn \pouya_boldfirst:n #1
{
\tl_set:Nn \l_pouya_input_tl { #1 }
\regex_replace_once:nnN { \A(.*?[A-Za-z]) } { \c{textbf}\cB\{ \1 \cE\} } \l_pouya_input_tl
\regex_replace_all:nnN { (\s) (.*?[A-Za-z]) } { \1 \c{textbf}\cB\{ \2 \cE\} } \l_pouya_input_tl
\tl_use:N \l_pouya_input_tl
}
\ExplSyntaxOff
\begin{document}
\BoldFirst{Gnu's Not Unix}
\BoldFirst{Gnu's Not \'Unix}
\BoldFirst{Gnu's Not \v{U}nix}
\end{document}
如果使用 LICR(无 UTF-8 输入),重音字符也可以工作。
答案3
使用分隔参数的 TeX 解决方案。
宏:
\BoldAbbrv{<text>}
并没有扩展其论点,并且\eBoldAbbrv{<text>}
在处理之前确实扩展了它的参数。
\BoldAbbrv
如果仅在其参数中使用纯文本,则宏就足够了。
\BoldAbbrv{\GNU}
检查和的输出\eBoldAbbrv{\GNU}
是否存在差异,以及处理重音的不同方式(至少需要用括号括起来)。
代码
\documentclass[a4paper]{article}
\makeatletter
\newcommand*{\BoldAbbrv}[1]{%
\qrr@BoldAbbrv#1 \relax
}
\newcommand*{\eBoldAbbrv}[1]{%
\edef\qrr@BoldAbbrv@Arg{#1 }%
\expandafter\qrr@BoldAbbrv\qrr@BoldAbbrv@Arg\relax
}
\def\qrr@BoldAbbrv#1 #2\relax{%
\textbf#1\relax
\ifx\relax#2\else
\space\qrr@BoldAbbrv#2\relax
\fi
}
\makeatother
\newcommand*{\GNU}{Gnu's Not Unix}
\begin{document}
\BoldAbbrv{Gnu's Not Unix} --- \BoldAbbrv{Gnu's Not {\"U}nix}
\BoldAbbrv{\GNU} --- \eBoldAbbrv{\GNU}
\BoldAbbrv{Gnu's Not {\d{U}}nix} --- \eBoldAbbrv{Gnu's Not {\noexpand\d{U}}nix}
\end{document}
输出
答案4
已经有 3 个答案,1 个被接受,但让我尝试以下代码。它确实允许用户输入一些灵活性,但在更危险的情况下肯定会失败。
更新以处理行尾...(以前的版本没有做预期的事情;附加的输出图像已更新以显示现在它可以工作。)
更新,以便左括号后的空格有意义(在以前的版本中,两个或两个以上的空格有意义并算作一个,但一个空格会被忽略)。
我也阐述了这个想法的局限性。很可能,这不是解决这个问题的好方法。
最终更新:我改进了一些部分。请参见下面的输出。
\documentclass[a4paper]{article}
%\newcommand{\BoldAbbrv}...IMPLEMENTATION
\usepackage{iftex}
\ifXeTeX\usepackage{fontspec}\fi
\ifLuaTeX\usepackage{fontspec}\fi
\ifPDFTeX\usepackage[utf8]{inputenc}\usepackage[T1]{fontenc}\fi
\def\LongJump#1#2#3#4{#2#3#4#1}
\makeatletter
{
\obeyspaces\catcode`\^^M\active\relax%
%
\gdef\BoldAbbrv@sp{\ifx\BoldAbbrv@token\bgroup%
\ \expandafter\textbf\else%
\ifx\BoldAbbrv@token\egroup\ \else%
\expandafter\expandafter\expandafter\BoldAbbrv@aux\fi\fi}%
%
\gdef\BoldAbbrv@eol{\ifx\BoldAbbrv@token\bgroup%
\ \expandafter\textbf\else%
\ifx\BoldAbbrv@token\egroup\ \else%
\expandafter\expandafter\expandafter\BoldAbbrv@auxeol\fi\fi}%
%
\gdef\BoldAbbrv@aux#1{\ifx#1 \expandafter \else%
\ifx#1^^M\expandafter\expandafter\expandafter \else%
\ifcat\noexpand#1a{\ \bfseries#1}\else%
\LongJump{\ #1}\fi\fi\fi}%
%
\gdef\BoldAbbrv@auxeol#1{\ifx#1 \expandafter^^M\else%
\ifx#1^^M\expandafter\expandafter\expandafter\BoldAbbrv@initpar\else%
\ifcat\noexpand#1a{\ \bfseries#1}\else%
\LongJump{\ #1}\fi\fi\fi}%
%
\gdef\BoldAbbrv@inita{\def {\futurelet\BoldAbbrv@token\BoldAbbrv@sp}%
\def^^M{\futurelet\BoldAbbrv@token\BoldAbbrv@eol}}%
%
\gdef\BoldAbbrv@auxb#1{\ifx#1 \expandafter \else%
\ifx#1^^M\expandafter\expandafter\expandafter \else%
\ifcat\noexpand#1a{\bfseries#1}\else%
\LongJump{#1}\fi\fi\fi}%
%
\gdef\BoldAbbrv@nosp{\ifx\BoldAbbrv@token\bgroup%
\expandafter\textbf\else%
\ifx\BoldAbbrv@token\egroup\else%
\expandafter\expandafter\expandafter\BoldAbbrv@auxb\fi\fi}%
%
\gdef\BoldAbbrv@initb{\futurelet\BoldAbbrv@token\BoldAbbrv@nosp}%
}
%
\def\BoldAbbrv@initpar{\par\BoldAbbrv@initb}
\def\BoldAbbrv#1#{\bgroup\obeyspaces\catcode`\^^M=\active
\BoldAbbrv@inita\afterassignment\BoldAbbrv@initb\let\next= }
\makeatother
\begin{document}\thispagestyle{empty}
GNU is a recursive acronym for ``\BoldAbbrv{Gnu's Not Unix}'', chosen
because GNU's design is Unix-like, but differs from Unix by being free
software and containing no Unix code.
Space Torture test (\emph{n.b.} spaces or endlines just after
the opening brace
or just
before the closing one count as one):
GNU is a recursive acronym for ``\BoldAbbrv{Gnu's Not Unix'',
}%
\BoldAbbrv {
chosen
because GNU's design is Unix-like, but differs from Unix
by being free
software and containing }no Unix code.
This new version does not bolden quotes, unfortunately it
doesn't either bolden the letter after the quote(s): \par
\BoldAbbrv{GNU is a recursive acronym for ``Gnu's Not Unix''}.
\BoldAbbrv{Things put {inside braces} in the argument will be
typeset {entirely bold.}
Accents need to be put inside braces. Omitting
braces does \emph{not} create an error. UTF8 letters in
PDFLaTeX need to be put inside braces for the macro to
operate, but this is not necessary with XeTeX. And in both
cases no compilation errors when braces are omitted.
Examples: Gnu's Not Unix --- Gnu's Not {\"U}nix (ok) ---
Gnu's Not \"Unix (no) --- Gnu's Not {Ü}nix (ok) --- Gnu's
Not Ünix (ok with Unicode engines). As ok followed a
parenthesis it was not boldened. The parenthesis not being a
letter wasn't boldened either.}
The new version has no effect on a macro even if it expands to
a string:
\newcommand*{\GNU}{Gnu's Not Unix}\BoldAbbrv{\GNU}.
This is sad but the new version is compatible with more
general input. It only boldens letters, or groups enclosed
within braces. \BoldAbbrv{{\GNU}}.
\BoldAbbrv{The macro {can} be applied to more than
one paragraph simultaneously. Yes, really.
The macro {can} be applied to more than
one paragraph simultaneously. Yes, really.
\BoldAbbrv{And it \BoldAbbrv{can be} nested. Although one does
not see the interest.}
}
An empty argument\BoldAbbrv{} is no error.
A space after the opening brace or before the closing brace
counts: \BoldAbbrv{ Gnu's Not {\"U}nix }X. Two or more are like
just one:\BoldAbbrv{ Gnu's Not {\"U}nix }X.
This does not quite work: \BoldAbbrv{\emph{An emphasized
argument.}} And this neither: \BoldAbbrv{\itshape{}An
italicized argument,\/} where we got rid of the initial
superfluous space which is in this example:
\BoldAbbrv{\itshape An italicized argument with a spurious
initial space.} So one must do {\itshape\BoldAbbrv{This is
not argument to a macro, and it works.}\/}
\textsl{\BoldAbbrv {The macro does not work in the argument to
another macro (except itself... as it is not really a
macro with argument despite appearances!)}}
\end{document}