我想使用keycommand
包定义命令或环境,并将列表传递给一个键。在我的例子中,这将是图像列表。
更明确地说:如何使以下伪代码起作用:
\documentclass{article}
\usepackage{keycommand}
\newkeyenvironment{images}[im]{
\begin{center}
\for \image in im
\includegraphics[width=2cm]{im}
}
{
\end{center}
}
\begin{document}
\begin{images}[
im={
image1,
image2,
image3
}
\end{images}
\end{document}
答案1
这里expl3
使用一个\seq
变量并将宏的内容拆分\commandkey{im}
为序列项,然后使用 map 函数循环遍历这些项,例如\loopfunction
基本上,任何可以拆分逗号分隔值列表的循环宏当然都可以。
从文档中可以keycommand
明显看出,这个包使用了etoolbox
,所以\forcsvlist
也可以使用宏。
\documentclass{article}
\usepackage{graphicx}
\usepackage{xparse}
\ExplSyntaxOn
\cs_generate_variant:Nn \seq_set_from_clist:Nn {Nx,NV}
\newcommand{\loopovercommandkey}[2][]{%
\seq_set_from_clist:Nx \l_tmpa_seq {#2}
\seq_map_inline:Nn \l_tmpa_seq {
\loopfunction[#1]{##1}%
}
}
\ExplSyntaxOff
\newcommand{\loopfunction}[2][]{%
\includegraphics[#1]{#2}
}
\usepackage{keycommand}
\newkeyenvironment{images}[im]{%
\begin{center}
\loopovercommandkey[width=2cm]{\commandkey{im}}
}
{%
\end{center}
}
\begin{document}
\begin{images}[%
im={
ente1,
ente2,
ente3
}]
\end{images}
\end{document}
答案2
以下代码定义并说明了宏\images{}
。\images
它接受一个强制参数,该参数应为选项的键值列表。最小合理列表将键设置images
为一个或多个图像文件的列表。
我已将其定义为宏,但可以轻松地使用它来定义\NewDocumentEnvironment{images}{m} ...
,或者在环境定义中使用它。
键值语法\images{<key-value options>}
:
images={<comma separated list of image files>}
grid=<columns>x<rows>
[默认为 2x2 网格)total width=<dimension>
[默认为\linewidth
]total height=<dimension>
[默认为\textheight
]
代码将总宽度和高度除以请求的行数和列数。然后将提供的每个图像设置在网格中一个矩形单元格的中心,第一个图像列在左上角,最后一个图像列在右下角。填充顶行,然后填充第二行,然后填充第三行,依此类推。
图像数量不必与网格大小匹配。TeX 将忽略网格中未指定图像的单元格。但是,如果列表中没有至少一个图像,您将收到错误,并且如果图像数量超出可用单元格,您将得到超出指定区域的内容(毫无疑问会出现坏框警告)。
如果需要,图像会缩放以适合网格单元格,同时保持原始图像的纵横比。但是,图像绝不会放大 - 仅在必要时缩小以适合。
例如,
\images{%
images={cath-gadael-chartref,cathod,cath,coeden-nadolig-2014},
}
\images{%
images={tspcd,cauldron,duck,tiger,example-image-c},
grid=3x2,
}
将生成 2 个整页网格。第一个默认为 2x2,每个单元格都有一个图像。第二个有 6 个单元格,第六个为空。
showframe
在这里用于显示页面的宽度和高度由网格单元格填充的方式。
请注意,垂直和水平间距是由于图像选择不当,无法满足可用网格单元的比例。为了使它们适合,它们会缩放,但为了避免扭曲,它们的纵横比会保持不变,从而在受影响的单元内创建垂直或水平填充。
完整代码:
\documentclass{article}
\usepackage{xparse,graphicx}
\usepackage[showframe]{geometry}
\ExplSyntaxOn
\seq_new:N \l_student_grid_seq
\int_new:N \l_student_cols_int
\int_new:N \l_student_rows_int
\int_new:N \g_student_count_int
\coffin_new:N \l_student_image_coffin
\coffin_new:N \l_student_images_coffin
\tl_new:N \l_student_graphics_tl
\cs_new_protected_nopar:Npn \student_gridset:n #1
{
\seq_set_split:Nnn \l_student_grid_seq { x } { #1 }
\seq_get_left:NN \l_student_grid_seq \l_tmpa_tl
\int_set:Nn \l_student_cols_int { \l_tmpa_tl }
\seq_get_right:NN \l_student_grid_seq \l_tmpa_tl
\int_set:Nn \l_student_rows_int { \l_tmpa_tl }
}
\cs_new_protected_nopar:Npn \student_graphics:nn #1 #2
{
\includegraphics [ #1 ] { #2 }
}
\cs_generate_variant:Nn \student_graphics:nn { Vn }
\cs_new_protected_nopar:Npn \student_boximage:nnn #1 #2 #3
{
\hbox_set:Nn \l_tmpa_box { \includegraphics { #3 } }
\dim_compare:nT { \box_ht:N \l_tmpa_box + \box_dp:N \l_tmpa_box > #2 }
{
\tl_put_right:Nn \l_student_graphics_tl { height = #2 , }
}
\dim_compare:nT { \box_wd:N \l_tmpa_box > #1 }
{
\tl_put_right:Nn \l_student_graphics_tl { width = #1 , }
}
\tl_put_right:Nn \l_student_graphics_tl { keepaspectratio = true }
\vcoffin_set:Nnn \l_student_image_coffin { #1 }
{
\vbox_to_ht:nn { #2 }
{
\skip_vertical:N \fill
\student_graphics:Vn \l_student_graphics_tl { #3 }
\skip_vertical:N \fill
}
}
}
\cs_generate_variant:Nn \student_boximage:nnn { VVn }
\cs_new_protected_nopar:Npn \student_processimages:nnnnn #1 #2 #3 #4 #5
{
\int_gzero:N \g_student_count_int
\dim_set:Nn \l_tmpa_dim { #1 / #3 }
\dim_set:Nn \l_tmpb_dim { #2 / #4 }
\clist_map_inline:nn { #5 }
{
\student_boximage:VVn \l_tmpa_dim \l_tmpb_dim { ##1 }
\int_compare:nTF { \g_student_count_int = 0 }
{
\coffin_join:NnnNnnnn \l_student_images_coffin { l } { b } \l_student_image_coffin { l } { t } { 0pt } { 0pt }
}
{
\coffin_join:NnnNnnnn \l_student_images_coffin { \l_student_image_coffin-r } { \l_student_image_coffin-t } \l_student_image_coffin { l } { t } { 0pt } { 0pt }
}
\int_gincr:N \g_student_count_int
\int_compare:nT { \g_student_count_int = \l_student_cols_int }
{ \int_gzero:N \g_student_count_int }
}
\coffin_typeset:Nnnnn \l_student_images_coffin { B } { l } { 0pt } { 0pt }
}
\cs_generate_variant:Nn \student_processimages:nnnnn { VVVVV }
\keys_define:nn { student / images }
{
grid .code:n = {
\student_gridset:n { #1 }
},
grid .initial:n = { 2 x 2 },
images .clist_set:N = \l_student_images_clist,
total~width .dim_set:N = \l_student_width_dim,
total~height .dim_set:N = \l_student_height_dim,
}
\NewDocumentCommand \images { m }
{
\group_begin:
\dim_set_eq:NN \parindent \c_zero_dim
\keys_set:nn { student / images } { total~width = \linewidth , total~height = \textheight }
\keys_set:nn { student / images } { #1 }
\student_processimages:VVVVV \l_student_width_dim \l_student_height_dim \l_student_cols_int \l_student_rows_int \l_student_images_clist
\group_end:
}
\ExplSyntaxOff
\begin{document}
\images{%
images={cath-gadael-chartref,cathod,cath,coeden-nadolig-2014},
}
\images{%
images={tspcd,cauldron,duck,tiger,example-image-c},
grid=3x2,
}
\end{document}
编辑
如果您确实需要一个images
环境并用作im
键的名称,那么您可以调整键的定义并将的定义更改\images
为images
环境。
例如,我们可以将密钥重命名images
为im
:
\keys_define:nn { student / images }
{
grid .code:n = {
\student_gridset:n { #1 }
},
grid .initial:n = { 2 x 2 },
im .clist_set:N = \l_student_images_clist,
total~width .dim_set:N = \l_student_width_dim,
total~height .dim_set:N = \l_student_height_dim,
}
并改变\images
到images
与问题类似的环境:
\NewDocumentEnvironment { images } { m }
{
\group_begin:
\begin{center}
\dim_set_eq:NN \parindent \c_zero_dim
\keys_set:nn { student / images } { total~width = \linewidth , total~height = \textheight }
\keys_set:nn { student / images } { #1 }
\student_processimages:VVVVV \l_student_width_dim \l_student_height_dim \l_student_cols_int \l_student_rows_int \l_student_images_clist
\skip_vertical:N \medskipamount
}{
\end{center}
\group_end:
}
与原始伪代码一样,这使用了一个center
围绕图像和环境内容的环境。(我认为环境的内容也应该被包装,否则根本没有理由使用环境。)
\images{images={<list>}}
然后,我们可以说,而不是, \begin{images}{im={<list>}} ... \end{images}
:
\begin{images}
{%
im={cath-gadael-chartref,cathod,cath,coeden-nadolig-2014},
}
\end{images}
\begin{images}
{%
im={tspcd,cauldron,duck,tiger,example-image-c},
grid=3x2,
total height=.5\textheight,
total width=.9\textwidth,
}
This is some content for the \verb|images| environment.
\end{images}
这里的主要区别在于环境采用的是强制参数,而不是可选参数。这似乎反映了预期用途:我们不希望参数是可选的,因为如果没有图像列表,环境就没有意义。
如果你确实希望该参数是可选的,那么你可以说,例如,
\NewDocumentEnvironment { images } { O { im = {example-image-a} } }
然后写
\begin{images}
[im={<image-1>,<image-2>,<image-3>,...}]
....
但这似乎并不理想,因为用户确实应该得到警告,而不是example-image-a
在没有指定图像的情况下。此外,在某些情况下,这会导致奇怪的错误,例如,如果用户提供了参数但没有提供值im
。您可以通过在其他地方指定默认列表或测试以查看键是否有值来避免这种情况。但这会在不太透明的用户界面的服务中引入许多复杂性,因此似乎总体上是一个双输的局面。