如何扩展 keyval 参数

如何扩展 keyval 参数

我正在尝试扩展一个参数的值,该参数将是文档中包含的页面范围,但我不知道该怎么做。起初,我得到了

错误:缺少为 \ifnum 插入的 =。

MWE 是

\documentclass{article}

\usepackage{pdfpages}
\newcommand{\myargval}{1-3}

\begin{document}
\includepdf[pages=1-3, clip]{example.pdf}
\includepdf[pages=\myargval, clip]{example.pdf}
\end{document}

评论者解释了如何使用 \edef 和 \noexpand 来评估参数的值。

然而,在完整版本中,我还有另一个pagecommand不应扩展的论点:

\documentclass{article}

\usepackage{pdfpages}
\newcommand{\myargval}{1-3}

\begin{document}
\includepdf[pages=1-3, clip, pagecommand={\thispagestyle{headings}}]{example.pdf}
\edef\temp{\noexpand\includepdf[pages=\myargval, clip, pagecommand={\thispagestyle{headings}}]{example.pdf}}\temp

\end{document}

如果我使用它,我会得到:

错误:不完整 \iffalse;第 10 行后的所有文本都被忽略。

答案1

您可以使用以下技巧来扩展您需要的内容,并通过在内容前面添加 来保持不应扩展的内容不扩展\noexpand

\documentclass{article}

\usepackage{pdfpages}
\newcommand{\myargval}{1-3}

\begin{document}
\includepdf[pages=1-3, clip, pagecommand={\thispagestyle{headings}}]{lipsum50}

\begingroup
\edef\x{\endgroup
        \noexpand\includepdf[pages=\myargval,
                             clip,
                             pagecommand={\noexpand\thispagestyle{headings}}]
                            {lipsum50}}\x

\end{document}

以一般形式添加一个群

\begingroup
\edef\x{\endgroup <stuff>}\x

确保\x不会继续存在。这不是必需的,但\x可以在使用之前将其定义为其他内容(一般而言),即在调用 之后恢复其含义 \x\edef扩展所有可扩展的内容(\endgroup不可扩展),同时\noexpand\<csname>扩展为\<csname>

lipsum50.pdf是一个最小文档,\lipsum[1-50]lipsum包裹

答案2

这是一个复杂的解决方案,但最终的界面更清晰。

我没有提供对所有键的支持,应该清楚如何扩充该集合。

\documentclass{article}
\usepackage{xparse}
\usepackage{pdfpages}

\ExplSyntaxOn
\NewDocumentCommand{\xincludepdf}{O{}m}
 {
  \group_begin:
  \keys_set:nn { xincludepdf } { #1 }
  \xincludepdf_include:Vn \l_xincludepdf_options_tl { #2 }
  \group_end:
 }
\cs_new_protected:Nn \xincludepdf_include:nn
 {
  \includepdf[#1]{#2}
 }
\cs_generate_variant:Nn \xincludepdf_include:nn { V }

\tl_new:N \l_xincludepdf_options_tl

\keys_define:nn { xincludepdf }
 {
  % pagecommand must not be expanded
  pagecommand .code:n = \tl_put_right:Nn \l_xincludepdf_options_tl { pagecommand=#1, },
  % all others should be fully expanded
  pages .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { pages=#1, },
  nup   .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { nup=#1, },
  landscape .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { landscape=#1 },
  landscape .default:n = true,
  delta .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { delta=#1, },
  offset .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { offset=#1, },
  frame .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { frame=#1, },
  frame .default:n = true,
  column .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { column=#1, },
  column .default:n = true,
  columnstrict .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { columnstrict=#1, },
  columnstrict .default:n = true,
  openright .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { openright=#1, },
  openright .default:n = true,
  clip .code:n = \tl_put_right:Nx \l_xincludepdf_options_tl { clip=#1, },
  clip .default:n = true,
  % ...
 }
\ExplSyntaxOff

\newcommand{\myargval}{1-3}

\begin{document}

\section{Whatever}

\xincludepdf[pages=\myargval,clip, pagecommand={\thispagestyle{headings}}]{kant}

\newcommand{\tpsh}{\thispagestyle{headings}}

\xincludepdf[pages=\myargval,clip, pagecommand=\tpsh]{kant}

\end{document}

答案3

您可以使用内置有该功能的 key=value 接口(我推荐expkv,但我是它的作者...其他内置有一些扩展控制支持的软件包是pgfkeysoptions。使用 . 在使用其他 key=value 解析器的现有宏周围添加包装器最简单expkv)。

不幸的是,您想将它用于已经使用另一个 key=value 接口的现有宏,因此我们必须稍微扩充一下这个宏(类似于 @egreg 的答案,但这里要简单得多)。为此,我们使用\ekvparse(它将应用下面描述的规则),然后使用两个辅助宏(\xincludepdf@k以及\xincludepdf@kv以下内容)重建 key=value 列表。

expkv提供了一种以简单方式指定键值扩展的机制。只需在键前加上前缀<rules>:(包括冒号后的空格),规则就会执行(在键=值对中,规则适用于值,如果没有给出=值,则适用于键)。内置了以下规则(在 2023-01-23 版本中):

  • o扩大价值一次
  • e充分扩大价值\expanded
  • c根据提供的值构建宏(使用\csname
  • f扩展值直到遇到不可扩展的标记(如果是空格,则删除该空格)
  • V该值应该是一个单独的标记,将该标记扩展为该值(如果它是一个宏,则有效o,如果它是一个寄存器,则将导致该寄存器的内容)
  • v类似于执行cV(但如果结果宏未定义,则结果不同)
  • s删除一组周围的空格和(如果之后所有内容都删除)括号
  • b在值周围添加一对括号
  • p{<stuff>}放置<stuff>在值之前
  • P{<stuff>}<stuff>值后面的位置
  • g吞噬第一个标记或括号组的值
  • \r将所有扩展的结果重新插入为附加的 key=value 输入
  • \key{<rules>}在 key=value 对中应用于键而不是值
  • R与...一样V\r
  • r与...一样v\r

多个规则从左到右连续应用,例外情况是,\r在处理完所有其他规则后,该规则始终会应用。

对于这种用例,我们只需要-rule o(或者可以改用-rule V)。

\documentclass{article}

\usepackage{pdfpages}
\usepackage{expkv}

\newcommand{\myargval}{1-3}

\makeatletter
\newcommand\xincludepdf[2][]
  {%
    \expandafter\includepdf
      % inner \expanded fully expands \ekvparse,
      % outer \expanded fully expands all \xincludepdf@k(v)
      \expanded{\expanded{[\ekvparse\xincludepdf@k\xincludepdf@kv{#1}]}}%
      {#2}%
  }
% needs to protect against the outer \expanded, so uses \unexpanded (we don't
% want to expand more than the explicitly stated rules need)
\newcommand\xincludepdf@k[1]{,\unexpanded{#1}}
% the space between the = and the value make this more robust for most key=value
% parsers
\newcommand\xincludepdf@kv[2]{,\unexpanded{#1= {#2}}}
\makeatother

\begin{document}
\includepdf[pages=1-3, clip]{example-image-duck.pdf}
% o: means expand the value once, the space after the colon is mandatory
\xincludepdf[o: pages=\myargval, clip]{example-image-duck.pdf}
\end{document}

不需要\ekvparse知道的任何键\includepdf,它只会处理您请求的扩展,其他每个解析都由\includepdf它自己处理,如果遇到未知的键,它会抱怨。

相关内容