LaTeX3 键值转发

LaTeX3 键值转发

假设我想编写一个命令igtest,其工作方式与\includegraphics来自相同graphicx,不同之处在于如果给出了页码,例如\igtest[page=5]{someimg},则页码将被 1 覆盖,即它应该像一样工作\includegraphics[page=1]{someimg}。(这是一个 MWE,所以不要问我为什么要这样做……)

我的尝试如下:

\documentclass{article}
\usepackage{graphicx}

\ExplSyntaxOn
\keys_define:nn { ig_keys } {
    page.tl_set:N   = \l_ig_page_tl
}
\tl_new:N \l__ig_unknown_keys_tl
\NewDocumentCommand{\igtest}{O{} m}
 {
    \keys_set_known:nnN { ig_keys }{ #1 }\l__ig_unknown_keys_tl
    \tl_if_empty:NTF{\l_ig_page_tl}{
        \includegraphics[#1]{#2} 
    }{
        \includegraphics[\tl_use:N\l__ig_unknown_keys_tl,page=1]{#2}
    }
 }
\ExplSyntaxOff


\begin{document}
\igtest[width=\textwidth,page=2]{example-image-a}
\end{document}

路线图很明确:单独捕获页面密钥,将其他密钥作为未知密钥存储在令牌列表中\l__ig_unknown_keys_tl,然后将其转发给\includegraphics

代码不起作用。显然,我没有\l__ig_unknown_keys_tl正确使用。

如何修复?或者可以修复吗(因为\includegraphics不是用 LaTeX3 编写的)?

答案1

使用\use:x

\documentclass{article}
\usepackage{graphicx}

\ExplSyntaxOn
\keys_define:nn { ig_keys } {
    page.tl_set:N   = \l_ig_page_tl
}
\tl_new:N \l__ig_unknown_keys_tl
\NewDocumentCommand{\igtest}{O{} m}
 {
    \keys_set_known:nnN { ig_keys }{ #1 }\l__ig_unknown_keys_tl
    \tl_if_empty:NTF{\l_ig_page_tl}{
        \includegraphics[#1]{#2} 
    }{
        \use:x{
            \exp_not:N\includegraphics[\exp_not:V \l__ig_unknown_keys_tl,page=1]{\exp_not:n{#2}}
        }
    }
 }
\ExplSyntaxOff


\begin{document}
\igtest[width=\textwidth,page=2]{example-image-a}
\end{document}

这会构建一些要执行的代码(作为参数中的 x 扩展),然后执行它。(要打印出实际执行的代码,只需更改\use:x\tl_show:x或类似内容。)

答案2

您可以定义 的内部副本\includegraphics,因此您可以定义其变体。

\documentclass{article}
\usepackage{graphicx}

\ExplSyntaxOn

\keys_define:nn { ig_keys }
  {
    page.tl_set:N = \l_ig_page_tl,
  }
\tl_new:N \l__ig_unknown_keys_tl

\cs_new_protected:Nn \__ig_ig:nnn { \includegraphics[#1,#2]{#3} }
\cs_generate_variant:Nn \__ig_ig:nnn { V }

\NewDocumentCommand{\igtest}{O{} m}
  {
    \group_begin:
    \keys_set_known:nnN { ig_keys }{ #1 } \l__ig_unknown_keys_tl
    \tl_if_empty:NTF \l_ig_page_tl
      {
        \__ig_ig:nnn { #1 } { } { #2 } 
      }
      {
        \__ig_ig:Vnn \l__ig_unknown_keys_tl { page=1 } { #2 }
      }
    \group_end:
  }

\ExplSyntaxOff


\begin{document}

\igtest[width=3cm,angle=30,page=2]{example-image-a}

\igtest[width=3cm,angle=30]{example-image-a}

\end{document}

请注意,除非您首先清除的可能值,否则您需要\group_begin:和。\group_end:\l_ig_page_tl

在此处输入图片描述

更加紧凑:

\ExplSyntaxOn

\keys_define:nn { ig_keys }
  {
    page.tl_set:N = \l_ig_page_tl,
  }
\tl_new:N \l__ig_unknown_keys_tl

\cs_new_protected:Nn \__ig_ig:nnn { \includegraphics[#1,#2]{#3} }
\cs_generate_variant:Nn \__ig_ig:nnn { Ve }

\NewDocumentCommand{\igtest}{O{} m}
  {
    \group_begin:
    \keys_set_known:nnN { ig_keys }{ #1 } \l__ig_unknown_keys_tl
    \__ig_ig:Ven \l__ig_unknown_keys_tl { \tl_if_empty:NF \l_ig_page_tl { page=1 } } { #2 }
    \group_end:
  }

\ExplSyntaxOff

答案3

您的问题与扩展有关(\includegraphics在传入键时“看不到”它们,而只能看到\tl_use:N\l__ig_unknown_keys_tl,page=1,因此尝试将其解析\tl_use:N\l__ig_unknown_keys_tl为单个键,但失败了),并且用户202729 的回答展示如何修复该问题(通过在扩展\l__ig_unknown_keys_tl之前扩展其值\includegraphics)。

以下使用expkv-cs替代方法来实现该过滤行为,这样就根本不需要扩展技巧了。但请注意,此机制仅适用于您想要以这种方式过滤的最多 8 个键(否则您将需要另一个宏,这expkv-cs可能再次需要扩展技巧……)。

\documentclass{article}
\usepackage{expkv-cs}
\usepackage{graphicx}

\ExplSyntaxOn
\NewDocumentCommand \igtest {O{} m} { \__ig_test_filter_page:nn {#1} {#2} }
\ekvcSplit \__ig_test_filter_page:nn
  {
    page=, % #1 will be the value of the page key, and empty if it was unused
    ...    % #2 will be all the unknown keys
  }
  {
    \tl_if_empty:nTF {#1}
      { \includegraphics [#2] }
      { \includegraphics [#2, page=1] }
    % the mandatory argument to \includegraphics will be curried
  }
\ExplSyntaxOff

\begin{document}
\includegraphics[width=5cm, page=1]{example-image-duck}

\includegraphics[width=5cm, page=5]{example-image-duck}

\igtest[width=5cm, page=5]{example-image-duck}
\end{document}

在此处输入图片描述

小问题:由于它扩展为不可扩展的宏,因此\__ig_test_filter_page:nn不会被定义,尽管它应该被定义。\protected\includegraphics

相关内容