我正在尝试用 LaTeX 构建一个发票生成系统,该系统将简单易维护。目前,我们正在使用 longtable 和预处理层。我想将其更改为一.sty
组宏,以便模板可以与输入其中的数据更紧密地分离。
我想要的是类似这样的 API:
\begin{invoicetable}{r|llrr|r}{runningnumber,description,qty,sellprice,linetotal}
\heading{runningnumber=\#,partnumber=Partnumber,description=Description,qty=Qty,sellprice=Price,linetotal=Total}
\line{runningnumber=1,partnumber=A-12232,description={Test part, some data},qty=4,sellprice=14.99,linetotal=59.96}
\end{invoicetable}
这将是类似这样的宏(为了清晰起见,这里添加了漂亮的格式):
\begin{longtable}{r|llrr|r}
\# & Partnumber & Description & Qty & Sellprice & linetotal \\
\endhead
1 & A-12232 & Test part, some data & 4 & 14.99 & 59.96 \\
\end{longtable}
这样做的几个原因是,它允许我删除环境声明中的列,并忽略行项目中的相关键,并且可以在添加行本身时释放键排序(partnumber = A-12232,qty = 4 无论排序如何都是相同的)。
此时,我对\line
宏在这种情况下的工作方式有了很好的了解(我可能会将其命名为\invoiceline
),如果我遇到问题,我可以在那里提出更多问题,但我的直接问题是宏的参数\begin
。我需要能够大致弄清楚如何在内部获取逗号分隔的值,将它们分解为有序列表和计数器,例如将计数器\invoice@cols
作为计数器,并将其\invoice@col@current
作为另一个计数器。我认为每个列都需要一个宏,例如\invoice@col1
返回列的名称。
答案1
这是一个涉及的解决方案xtring
:
\documentclass{article}
\usepackage{xstring,longtable}
\makeatletter
\def\line#1{%
\global\let\tab@keys@i\tab@keys
\gdef\line@content{#1,}%
\line@i
}
\def\line@i{%
\StrCut\tab@keys@i,\current@key\tab@keys@i
\global\let\tab@keys@i\tab@keys@i
\IfSubStr\line@content\current@key
{\StrBehind\line@content\current@key[\current@key]%
\StrBetween\current@key=,%
}
\relax
\ifx\@empty\tab@keys@i\\%
\else&\expandafter\line@i
\fi
}
\def\heading#1{\line{#1}\endhead}
\newenvironment{invoicetable}[2]%
{\edef\tab@keys{#2,}%
\expandarg\noexploregroups
\longtable{#1}%
}
\endlongtable
\makeatother
\begin{document}
\begin{invoicetable}{r|llrr|r}{runningnumber,description,qty,sellprice,linetotal}
\heading{runningnumber=\#,partnumber=Partnumber,description=Description,qty=Qty,sellprice=Price,linetotal=Total}
\line{runningnumber=1,partnumber=A-12232,description={Test part, some data},qty=4,sellprice=14.99,linetotal=59.96}
\end{invoicetable}
\end{document}
答案2
这是使用 LaTeX3 宏的实现。
\documentclass{article}
\usepackage{longtable}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentEnvironment{invoicetable}{mm} % #1 = columns, #2 = column names
{
\travers_invoicetable:n { #2 }
}
{
\begin{longtable}{#1}
\l_travers_tablehead_tl
\hline
\endhead
\l_travers_table_tl
\end{longtable}
}
\NewDocumentCommand{\theading}{m}
{
\travers_makeline:Nn \l_travers_tablehead_tl { #1 }
}
\NewDocumentCommand{\tline}{m}
{
\travers_makeline:Nn \l_travers_table_tl { #1 }
}
\tl_new:N \l_travers_tablehead_tl
\tl_new:N \l_travers_table_tl
\tl_new:N \l__travers_linetemp_tl
\tl_new:N \l_travers_lastcol_tl
\seq_new:N \l_travers_colnames_seq
%%% what to do with unknown keys
\keys_define:nn { travers/invoice }
{
unknown .code:n =
}
% absorb the list of column names and define the keys
\cs_new_protected:Npn \travers_invoicetable:n #1
{
\seq_set_split:Nnn \l_travers_colnames_seq { , } { #1 }
\seq_map_inline:Nn \l_travers_colnames_seq
{
\keys_define:nn { travers/invoice }
{
##1 .tl_set:c = { l__travers_name_##1_tl }
}
}
% detach the last column
\seq_pop_right:NN \l_travers_colnames_seq \l_travers_lastcol_tl
}
% each line processes the key-value pairs and adds to the table
\cs_new_protected:Npn \travers_makeline:Nn #1 #2
{
\tl_clear:N \l__travers_linetemp_tl
\keys_set:nn { travers/invoice } { #2 }
\seq_map_inline:Nn \l_travers_colnames_seq
{
\tl_put_right:Nv \l__travers_linetemp_tl { l__travers_name_##1_tl }
\tl_put_right:Nn \l__travers_linetemp_tl { & }
}
\tl_put_right:Nv \l__travers_linetemp_tl { l__travers_name_\l_travers_lastcol_tl _tl }
\tl_put_right:Nn \l__travers_linetemp_tl { \\ }
\tl_put_right:NV #1 \l__travers_linetemp_tl
}
\cs_generate_variant:Nn \tl_put_right:Nn { Nv }
\ExplSyntaxOff
\begin{document}
\begin{invoicetable}{r|llr|r}{
runningnumber,
description,
qty,
sellprice,
linetotal
}
\theading{
runningnumber=\#,
partnumber=Part number,
description=Description,
qty=Qty,
sellprice=Price,
linetotal=Total
}
\tline{
runningnumber=1,
partnumber=A-12232,
description={Test part, some data},
qty=4,
sellprice=14.99,
linetotal=59.96
}
\end{invoicetable}
\begin{invoicetable}{r|llrr|r}{
runningnumber,
partnumber,
description,
qty,
sellprice,
linetotal
}
\theading{
runningnumber=\#,
partnumber=Part number,
description=Description,
qty=Qty,
sellprice=Price,
linetotal=Total
}
\tline{
runningnumber=1,
partnumber=A-12232,
description={Test part, some data},
qty=4,
sellprice=14.99,
linetotal=59.96
}
\end{invoicetable}
\begin{invoicetable}{r}{
linetotal
}
\theading{
runningnumber=\#,
partnumber=Part number,
description=Description,
qty=Qty,
sellprice=Price,
linetotal=Total
}
\tline{
runningnumber=1,
partnumber=A-12232,
description={Test part, some data},
qty=4,
sellprice=14.99,
linetotal=59.96
}
\end{invoicetable}
\end{document}