如何制作一个可以使用参数中的数据填充表格行的宏

如何制作一个可以使用参数中的数据填充表格行的宏

我正在尝试为简历文档创建一个类。作为该文档的一部分,将有一个包含所有教育信息的表格。我试图实现的是拥有某种模板来指定这些信息块之一是什么样子,然后拥有一个宏来使用相关数据实例化其中一个块。我目前的尝试是尝试使用它来处理将数据pgfkeys传递到宏中。该类的精简版本如下所示:

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{cv}

\LoadClass[10pt,a4paper]{article}

\RequirePackage{tabularx}
\RequirePackage{calc}
\RequirePackage{xparse}

\makeatletter
\protected\def\z#1{\pdfsavepos\write\@auxout{\gdef\string\tpos#1{\the\pdflastxpos}}}%
\def\foo#1#2{\ifcsname tpos#1\endcsname\the\dimexpr\csname tpos#2\endcsname sp -\dimexpr\csname tpos#1\endcsname sp\relax\fi}
\makeatother

\RequirePackage{pgfkeys}
\pgfkeys{
    /education/.is family, /education,
    title/.estore in=\educationtitle,
    programme/.estore in=\educationprogramme,
    notes/.estore in=\educationnotes,
    notes/.default={},
    institute/.estore in=\educationinstitute,
    thesisheader/.estore in=\educationthesisheader,
    thesisheader/.default=Thesis:,
    thesistitle/.estore in=\educationthesistitle,
    selectedcourses/.estore in=\educationselectedcourses,
    selectedcourses/.default={},
    dates/.estore in=\educationdates,
}

\newenvironment{education}{
    \setlength{\tabcolsep}{5pt}
    \setlength{\extrarowheight}{0pt}
    \begin{tabular}{!{\z{a}}r!{\z{b}}p{(\textwidth-\foo{a}{b})-\tabcolsep}}
}{
    \end{tabular}
}

\NewDocumentCommand{\educationitem}{o}{
    \pgfkeys{/education,notes,selectedcourses,thesisheader,#1}
    \educationdates{}           & \textbf{\educationtitle{}}        \\
                                & \educationprogramme{}             \\
                                & \educationnotes{}                 \\
                                & \textit{\educationinstitute{}}    \\
    \educationthesisheader{}    & \educationthesistitle{}           \\
    Selected courses:           & \educationselectedcourses{}       \\
}

(列的宽度有一些特殊之处,例如第二列填充了剩余的可用宽度。这是我前段时间从这里的其他答案中得到的。)

理想情况下我会使用如下类:

\documentclass{cv}

\begin{document}
\begin{education}
\educationitem[
    title={Master of Science},
    programme={Some programme},
    notes={Some notes about the degree},
    institute={Some institute},
    dates={2014--2017},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]{}


\educationitem[
    title={Bachelor of Science},
    programme={Some other programme},
    notes={Some more notes about the degree},
    institute={Some other institute},
    dates={2011--2014},
    thesisheader={Honours thesis:},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]{}
\end{education}
\end{document}

这有望产生如下所示的表格:

在此处输入图片描述

不幸的是,我得到了一个Undefined control sequence关于宏使用的错误\educationitem。我刚刚问一个问题关于这一点,但我把情况简化了太多,导致答案不符合我的实际意图。(XY问题再次出现......)

所以这是我真正的问题:我如何构造一个可以大致如上所述使用的宏来得到我想要的结果?使用的解决方案pgfkeys略有优势,但我对目前拥有的解决方案并不特别在意。希望这次我提供了正确数量的信息!

答案1

您可以

参数转发

使用 PGFkeys 也是如此。

我也更喜欢值键,即直接存储值的键。这意味着,/path/to/key可以通过以下方式轻松检索键的值

\pgfkeysvalueof{/path/to/key}

代替

\macroForKey

(在内部,这当然也只是一个宏,即\pgfk@/path/to/key。)

PGFkeys 已经使用宏来表示键如何处理其值,为什么不使用 PGFkeys 来存储实际值呢?

安装此类密钥的处理程序是.initial您还可以声明其初始值。这种类型的键还允许我们使用/.add/.prefix/.append我们可以用它在键中放入“更多”的值。

我也在使用

\pgfqkeys{/education}{<kv>}

这是稍快一点​​的版本

\pgfkeys{/education/.cd,<kv>}

在 的定义中\educationset


然后,在定义中我们在每个单元格中与路径结合\educationitem使用。#1\pgfkeys/education

这意味着,对于每个单元格,所有的值都会被一遍又一遍地设置(并且忘记了通过单元格末尾的 TeX 来检索),并且实际上只检索到一个 - 我们在该单元格中需要的那个。

PGFkeys 还附带一个密钥过滤机制,我们可以在这里使用它(参见下面代码中的选项 A),但我怀疑我们是否能从中获得什么,因为我们的所有密钥都只是设置一个值。(如果密钥可以进行复杂的计算和赋值等,我们当然不想这样做九次。)

我不太熟悉,xparse这就是我使用普通 LaTeX2e 的原因\providecommand

cv.cls

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{cv}
\LoadClass[10pt,a4paper]{article}
\RequirePackage{tabularx}
\RequirePackage{pgfkeys}
\providecommand\educationvaluefilter[2]{%
  % Option A: with a filter, discards all other keys
  % probably quicker when there's much more
  % to do than just setting a value
%  \pgfkeys{/pgf/key filters/equals/.install key filter=/education/#2}%
%  \pgfqkeysfiltered{/education}{#1}%
  % Option B: just setting all values
  \educationset{#1}%
  % and then type it:
  \pgfkeysvalueof{/education/#2}}
\providecommand*\educationset{\pgfqkeys{/education}}
\educationset{
  title/.initial=,
  programme/.initial=,
  notes/.initial=,
  institute/.initial=,
  thesisheader/.initial=Thesis:,
  thesistitle/.initial=,
  selectedcourses/.initial=,
  dates/.initial=,
}

\newenvironment{education}{
  \setlength{\tabcolsep}{5pt}%
  \setlength{\extrarowheight}{0pt}%
  \tabularx{\linewidth}{rX}
    }{
  \endtabularx
}
\providecommand{\educationitem}[1][]{
  \educationvaluefilter{#1}{dates}        & \textbf{\educationvaluefilter{#1}{title}}     \\
                                          & \educationvaluefilter{#1}{programme}          \\
                                          & \educationvaluefilter{#1}{notes}              \\
                                          & \textit{\educationvaluefilter{#1}{institute}} \\
  \educationvaluefilter{#1}{thesisheader} & \educationvaluefilter{#1}{thesistitle}        \\
  Selected courses:                       & \educationvaluefilter{#1}{selectedcourses}    \\
}

答案2

您的问题是每个单元格内部都形成一个组,因此由于仅提供本地分配,因此在第一个之后tabular您的宏定义就会丢失。&\educationitempgfkeys

下面使用另一个 key=value 包,即expkv-cs,它不必面对这个限制,因为它通过参数转发而不是分配来工作,从而使表构建变得更加容易(尽管与中的众多不同键处理程序相比,界面有些有限pgfkeys,但对于简单地收集和重新排序值来说已经足够了)。

我也使用过tabularx你的education表格,这似乎比使用的方法简单得多\pdfsavepos。另外,在你的类中定义诸如\z或之类的宏\foo来执行非常具体的事情是个坏主意(定义单字母宏大多数时候也是一个坏主意)。

title此外,如果省略、institute或,则会引发错误thesistitle,并且如果非强制键为空,则不会打印空行。

cv.cls

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{cv}

\LoadClass[10pt,a4paper]{article}

\RequirePackage{tabularx}
\RequirePackage{xparse}

\RequirePackage{expkv-cs}
% to grab an optional argument
\NewDocumentCommand\educationitem{O{}}{\educationitem@kv{#1}}
\newcommand\educationitem@ifnotempty[2]
  {%
    \if\relax\detokenize\expanded{{\ekvcValue{#1}{#2}}}\relax
      \expandafter\@gobbletwo
    \fi
    \@firstofone
  }
\newcommand*\educationitem@required[1]
  {\ClassError{cv}{The `#1' key is required}{}}
\ekvcHash\educationitem@kv
  {% list of default values, you could later change them using
   % \ekvcChange\educationitem@kv{<key>=<value>-list}
     title = \educationitem@required{title}
    ,programme = {}
    ,notes = {}
    ,institute = \educationitem@required{institute}
    ,thesisheader = Thesis:
    ,thesistitle = \educationitem@required{thesistitle}
    ,selectedcourses = {}
    ,dates = {}
  }
  {% to be used inside a tabular(x), so no need to pay attention for stray
   % spaces
    \ekvcValue{dates}{#1}        & \ekvcValue{title}{#1}              \\
    \educationitem@ifnotempty{programme}{#1}{& \ekvcValue{programme}{#1} \\}
    \educationitem@ifnotempty{notes}{#1}{& \ekvcValue{notes}{#1} \\}
                                 & \textit{\ekvcValue{institute}{#1}} \\
    \ekvcValue{thesisheader}{#1} & \ekvcValue{thesistitle}{#1}        \\
    \educationitem@ifnotempty{selectedcourses}{#1}
      {Selected courses: & \ekvcValue{selectedcourses}{#1} \\}
  }

\NewDocumentEnvironment{education}{+b}
  {%
    \setlength{\tabcolsep}{5pt}%
    \setlength{\extrarowheight}{0pt}%
    \par\noindent
    \begin{tabularx}{\linewidth}{rX}%
      #1%
    \end{tabularx}%
  }{}

main.tex

\documentclass{cv}

\begin{document}
\begin{education}
\educationitem[
    title={Master of Science},
    programme={Some programme},
    notes={Some notes about the degree},
    institute={Some institute},
    dates={2014--2017},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]


\educationitem[
    title={Bachelor of Science},
    programme={Some other programme},
    notes={Some more notes about the degree},
    institute={Some other institute},
    dates={2011--2014},
    thesisheader={Honours thesis:},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]
\end{education}
\end{document}

结果

在此处输入图片描述

答案3

的问题在于\educationitem它是在表格单元格内执行的,而表格单元格构成局部范围。因此,在.estore第一个之后,所有的局部定义都消失了&。您可以使用\expanded- -trickery 在-environment“看到”\unexpanded之前获取所有临时宏的顶层扩展——通常不需要,但我看到了激活的代码,所以……tabular&\noexpand&&

\documentclass[10pt,a4paper]{article}

\RequirePackage{tabularx}
\RequirePackage{calc}
\RequirePackage{xparse}

\makeatletter
\protected\def\z#1{\pdfsavepos\write\@auxout{\gdef\string\tpos#1{\the\pdflastxpos}}}%
\def\foo#1#2{\ifcsname tpos#1\endcsname\the\dimexpr\csname tpos#2\endcsname sp -\dimexpr\csname tpos#1\endcsname sp\relax\fi}
\makeatother

\RequirePackage{pgfkeys}
% Empty defaults in case keys for defining the macros are not used at all:
\newcommand*\educationtitle{}%
\newcommand*\educationprogramme{}%
\newcommand*\educationnotes{}%
\newcommand*\educationinstitute{}%
\newcommand*\educationthesisheader{}%
\newcommand*\educationthesistitle{}%
\newcommand*\educationselectedcourses{}%
\newcommand*\educationdates{}%
\pgfkeys{
    /education/.is family, /education,
    title/.estore in=\educationtitle,
    programme/.estore in=\educationprogramme,
    notes/.estore in=\educationnotes,
    notes/.default={},
    institute/.estore in=\educationinstitute,
    thesisheader/.estore in=\educationthesisheader,
    thesisheader/.default=Thesis:,
    thesistitle/.estore in=\educationthesistitle,
    selectedcourses/.estore in=\educationselectedcourses,
    selectedcourses/.default={},
    dates/.estore in=\educationdates,
}

\newenvironment{education}{%
    \setlength{\tabcolsep}{5pt}%
    \setlength{\extrarowheight}{0pt}%
    \begin{tabular}{!{\z{a}}r!{\z{b}}p{(\textwidth-\foo{a}{b})-\tabcolsep}}%
}{%
    \end{tabular}%
}
\NewDocumentCommand{\educationitem}{o}{%
  %\begingroup
  \pgfkeys{/education,notes,selectedcourses,thesisheader,#1}%%%%%%%%
  \expanded{%
    %\endgroup
    \unexpanded\expandafter{\educationdates}\noexpand&\noexpand\textbf{\unexpanded\expandafter{\educationtitle}}\noexpand\\
    \noexpand&\unexpanded\expandafter{\educationprogramme}\noexpand\\
    \noexpand&\unexpanded\expandafter{\educationnotes}\noexpand\\
    \noexpand&\noexpand\textit{\unexpanded\expandafter{\educationinstitute}}\noexpand\\
    \unexpanded\expandafter{\educationthesisheader}\noexpand&\unexpanded\expandafter{\educationthesistitle}\noexpand\\
    \unexpanded{Selected courses:}\noexpand&\unexpanded\expandafter{\educationselectedcourses}\noexpand\\
  }%
}


\begin{document}
\begin{education}
\educationitem[
    title={Master of Science},
    programme={Some programme},
    notes={Some notes about the degree},
    institute={Some institute},
    dates={2014--2017},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]
\\
\educationitem[
    title={Bachelor of Science},
    programme={Some other programme},
    notes={Some more notes about the degree},
    institute={Some other institute},
    dates={2011--2014},
    thesisheader={Honours thesis:},
    thesistitle={The title of the thesis},
    selectedcourses={Some list of courses that might get pretty long, filling out the usable space of the row.},
]
\end{education}
\end{document}

在此处输入图片描述

相关内容