我正在编写发票类,我想tex
尽可能保持实际文件干净,以便代码不会吓跑非技术人员。
我的最终目标是在invoice.cls
文件中定义一个变量,然后通过一个非常简单干净的命令为其设置一个值main.tex
。类似于\address{Street, Number}{ZIP Code Town}{Country}
和\name{First name}{Last name}
和\bankDetails{IBAN number}{Bank Swift Code}
请记住,我必须在invoice.cls
文件中使用这些变量的值。
目前,我在类文件中定义了一个新命令,并为其提供了一些可选参数。通过调用命令并传递参数,我得到了类似上面的代码。问题是,我需要在多个地方访问名称变量,并且我不希望最终用户必须多次输入他的名字作为参数……最好将这些变量定义在全局,并在需要时访问它们。
就像
\newcommand{\firstName}{John}
\newcommand{\lastName}{Doe}
\newcommand{\name}{\firstName\,\lastName}
但你会明白
\name{John}{Doe}
会干净得多。
答案1
我非常喜欢expl3
它的property
“变量”功能,即key
基于列表的数据存储。只要键的数量不是太大,prop
列表就非常方便。
键可能是FirstName
或Zip
。
宏\StoreInvoiceData
将给定的键存储为等FirstName=foo
,放入属性列表的IBAN=US345342342
相关槽中,此处命名。宏的键值接口不能与属性列表的 混淆,尽管后台的存储机制是相同的。名称与属性键名称相同,这不是强制性的,但为了方便记住名称。key
\g_sam_invoice_prop
keys
key=
指定顺序根本不重要。
例如使用\GetInvoiceData{foo}
来提取foo
密钥的值。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_sam_invoice_prop
\cs_new:Nn \sam_store_property:nn {
\prop_gput:Nnn \g_sam_invoice_prop {#1} {#2}
}
%Defining key-value interface
\keys_define:nn {saminvoice} {
Street .code:n = {\sam_store_property:nn {Street}{#1} },
Number .code:n = {\sam_store_property:nn {Number}{#1} },
Zip .code:n = {\sam_store_property:nn {Zip}{#1} },
Country .code:n = {\sam_store_property:nn {Country}{#1} },
FirstName .code:n = {\sam_store_property:nn {FirstName}{#1} },
SecondName .code:n = {\sam_store_property:nn {SecondName}{#1} },
IBAN .code:n = {\sam_store_property:nn {IBAN}{#1} },
BIC .code:n = {\sam_store_property:nn {BIC}{#1} },
}
\NewDocumentCommand{\StoreInvoiceData}{+m}{
\prop_gclear:N \g_sam_invoice_prop% Clearing the property list
% Set the keys
\keys_set:nn {saminvoice}{#1}
}
\NewDocumentCommand{\GetInvoiceData}{m}{
\prop_if_in:NnTF \g_sam_invoice_prop {#1}
{% True
\prop_item:Nn \g_sam_invoice_prop {#1}% Extract the key #1 from the property list
}{% False
Unknown
}
}
\ExplSyntaxOff
\begin{document}
\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34}}
This is an invoice for \GetInvoiceData{FirstName}\ \GetInvoiceData{SecondName} about the manufacturing and delivery of 100 Mage staffs. Please pay to \GetInvoiceData{IBAN}
\end{document}
检查是否给出密钥的版本
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_sam_invoice_prop
\cs_new:Nn \sam_store_property:nn {
\prop_gput:Nnn \g_sam_invoice_prop {#1} {#2}
}
%Defining key-value interface
\keys_define:nn {saminvoice} {
Street .code:n = {\sam_store_property:nn {Street}{#1} },
Number .code:n = {\sam_store_property:nn {Number}{#1} },
Zip .code:n = {\sam_store_property:nn {Zip}{#1} },
Country .code:n = {\sam_store_property:nn {Country}{#1} },
FirstName .code:n = {\sam_store_property:nn {FirstName}{#1} },
SecondName .code:n = {\sam_store_property:nn {SecondName}{#1} },
IBAN .code:n = {\sam_store_property:nn {IBAN}{#1} },
BIC .code:n = {\sam_store_property:nn {BIC}{#1} },
CompanyName .code:n= {\sam_store_property:nn {CompanyName}{#1} },
ClientName .code:n= {\sam_store_property:nn {ClientName}{#1} }
}
\NewDocumentCommand{\StoreInvoiceData}{+m}{
\prop_gclear:N \g_sam_invoice_prop% Clearing the property list
% Set the keys
\keys_set:nn {saminvoice}{#1}
}
\NewDocumentCommand{\GetInvoiceData}{m}{
\prop_if_in:NnTF \g_sam_invoice_prop {#1}
{% True
\prop_item:Nn \g_sam_invoice_prop {#1}% Extract the key #1 from the property list
}{% False
Unknown
}
}
% Normally better with a \prg_new_condiditional:, but for simplicity
% Check if the field is given and execute #2 for true case, otherwise #3
\NewDocumentCommand{\IfInvoiceFieldGivenTF}{m+m+m}{%
\prop_if_in:NnTF \g_sam_invoice_prop {
#1% Check to check
}{% True #2
#2
}{% False
#3
}
}% End of \IfInVoiceFieldGivenT
% Do something only if #1 is given
\NewDocumentCommand{\IfInvoiceFieldGivenT}{m+m}{%
\prop_if_in:NnT \g_sam_invoice_prop {
#1% Check to check
}{% True #2
#2
}%
}% End of \IfInVoiceFieldGivenT
\ExplSyntaxOff
\begin{document}
\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34},CompanyName={LembasBread Ltd.}, ClientName={Elrond}}
This is an invoice for \GetInvoiceData{FirstName}\ \GetInvoiceData{SecondName} about the manufacturing and delivery of 100 Mage staffs. Please pay to \GetInvoiceData{IBAN}
\textbf{Long version:}
\IfInvoiceFieldGivenTF{CompanyName}{%
\GetInvoiceData{CompanyName} c/o \GetInvoiceData{ClientName}
}{%
\GetInvoiceData{ClientName}%
}%
\textbf{Short version:}
\IfInvoiceFieldGivenT{CompanyName}{%
\GetInvoiceData{CompanyName} c/o{}% Extra space on purpose here
}
\GetInvoiceData{ClientName}
Clearing Data
\StoreInvoiceData{FirstName=Gandalf,SecondName={The Grey},Zip={The Nine},Country={Middle Earth}, IBAN={MORDOR6.60-34},ClientName={Elrond}}
\textbf{Long version:}
\IfInvoiceFieldGivenTF{CompanyName}{%
\GetInvoiceData{CompanyName} c/o \GetInvoiceData{ClientName}
}{%
\GetInvoiceData{ClientName}%
}%
\textbf{Short version:}
\IfInvoiceFieldGivenT{CompanyName}{%
\GetInvoiceData{CompanyName} c/o{}% Extra space on purpose here
}
\GetInvoiceData{ClientName}
\end{document}
答案2
看一下数据的定义\maketitle
:
\def\title#1{\gdef\@title{#1}} \def\@title{\@latex@error{No \noexpand\title given}\@ehc} \def\author#1{\gdef\@author{#1}} \def\@author{\@latex@warning@no@line{No \noexpand\author given}} \def\date#1{\gdef\@date{#1}} \gdef\@date{\today}
\title{My Title}
存储My Title
在全局宏中\@title
。后者用于\maketitle
。如果用户忘记调用\title
,\@title
则以抛出错误消息的方式初始化。类文件将\ClassError{class name}{Error message}
代替使用\@latex@error
。示例:
\ProvidesClass{foobar}[2017/08/05 v1.0 ...]
...
\newcommand*{\name}[2]{%
\gdef\@firstname{#1}%
\gdef\@lastname{#2}%
}
\def\@firstname{%
\ClassError{foobar}{No \noexpand\name given.}%
}
\def\@lastname{%
\ClassError{foobar}{No \noexpand\name given.}%
}
用法:
\documentclass[...]{foobar}
...
\name{First name}{Last name}
然后\@firstname
就\@lastname
可以使用(例如,通过类宏)。