相关答案是软件包作者转向 LaTeX3和LaTeX3 中已经实现了哪些新功能?我当前的文档(包含许多软件包)是否仍可以使用 LaTeX3 进行编译?
怎么xtemplate
正确使用?我已经阅读了软件包文档和 Lars Hellström 的关于模板的一些说明,我试图理解xfrac
执行。
我了解到有“对象”、“模板”、“键”和“实例”。但它们的交互方式令人困惑,并且xfrac
有太多选项难以掌握(并且是假设读者理解xtemplate
,而不是作为xtemplate
教程编写的)。
xtemplate
能否用一个简单但完整的例子来描述这些概念?
答案1
完整示例
在模板思想的首次实现中,有一个完整的示例试图解决底层思想。仍然可以template.dtx
在网上找到文档,例如,这里(在目录中/doc/latex/xpackages/xbase/
)。这是针对不同的实现,但基本概念没有改变,因此可能仍然有用。不知何故在重新实现期间,xtemplate.dtx
这部分文档被删除了(现在该部分是“空的”)。
概念解释
使用一些图片可以最好地解释其中实现的概念xtemplate
(我现在认为它是第二级原型)。以下是我认为 LaTeX3 架构应该是什么样的粗略草图:
该xtemplate
软件包构建了“对象类型存储库”、“模板存储库”、“类功能规范”和“类布局规范”的概念。但它没有与所谓的“LaTeX 数据库 (LDB)”交互,因为在完成主要工作时,xtemplate
我们认为 LDB 背后的概念无法实际实现,所以我们将其放在一边,并尝试在没有它的情况下实现解决方案,这在很大程度上就是当前的xtemplate
代码。
好吧,我想这并没有更好地告诉您这一切是怎么回事。 :-) 所以让我们尝试从中间部分向外解释。 那里我们有“排版元素层”,其思想是它描述了所有“排版元素”,即接受输入(0 个或更多)并对其进行处理以产生“排版结果”的对象。
排版设计层
现在的想法是,这些“排版元素”可以抽象地描述为
- 他们接受的输入(以及它们的语义含义)
- 以及它们所产生内容的语义(为了更好地表达)
- 但不是视觉表现
这就是我们所说的 ObjectType,它们最终会进入“对象类型存储库”。例如,考虑一个“标题”对象类型,它接受许多输入,例如标题标题、目录标题、是否编号等(其中一些输入可能是特殊值,如“NoValue”),但出于速度等原因,当前实现假设每种类型始终具有固定数量的参数,并且它们是位置相关的。
到目前为止,这还没有说明应该如何将事物可视化。它只是给出了抽象的功能元素。
对于每个 ObjectType,在“模板存储库”中都有多个模板(一个或多个),它们都实现了元素的可视化,即它们进行排版。它们之所以被称为模板,是因为它们旨在提供一定的灵活性来提供设计选择。设计是否分散在不同的模板上或在一个非常复杂的模板中实现,这取决于个人品味和实用性,例如,可能有一个用于生成显示标题的模板和一个用于生成插入标题的模板,或者尝试使用一个可以生成两种布局的更复杂的模板(如 LaTeX2e 的\@startsection
尝试)。
xtemplate
提供一个命令 ( \DeclareTemplateInterface
) 来声明模板接口,该接口基本上是模板提供的用于操作其实现的设计的旋钮和小工具的描述。它以键列表的形式给出,可以有多种输入类型。如果您喜欢设计师这边的工作:要实例化模板,设计师需要为这些键赋予值。
它还提供了模板代码 ( ) 的声明\DeclareTemplateCode
,该模板代码实现将界面键值作为输入以及来自 ObjectType 的文档参数的设计。此编码将使用层expl3
(即“核心语言层”)以及我称之为“排版基础层”(目前尚不存在)的额外帮助来完成。
所有这些都在“模板存储库”框中。
现在,如果没有可用的模板实现设计人员正在寻找的设计,那么必须首先编写另一个模板来实现 ObjectType 所需的设计,否则设计人员的任务是选择一个合适的模板并用一些值启动它来实现想要的结果。
类设计层
向上移动堆栈,我们在“类设计层”中有两个部分:“类功能规范”和“类布局规范”。请注意,此层不处理文档级语法。我们仍然处于(命名)ObjectTypes 级别,它们以标准化形式接受输入。
类别功能规范
“类功能规范”描述了文档类提供的逻辑元素是什么,例如,它提供以下标题级别:“A-head”、“B-head”、一直到“F-head”,或者用 2e 术语来说“section”、“subsection”等,一直到“subparagraph”。
它还应该描述关系(实际上还没有存在),所以本质上这个想法是提供一种“类 DTD”。
这个想法是,如果一个文档属于某个文档类(如此功能规范所述),那么可以通过将符合功能规范的布局规范替换为另一个符合此功能规范的布局规范,将其可视化从一种形式更改为另一种形式。
换句话说,某种功能规范应该对许多文档类布局通用,这样就可以使这些布局可以互换,而无需触及文档。我们知道,这在 2e 中是部分可能的,但通常并不完全可能,例如,article.cls
可以替换为amsart.cls
,但有些事情会失败,因为 DTD 并不完全相同(并且在 2e 中,DTD 与可视化没有分离,两者都在同一个地方完成,即在文件中,.cls
所以这并不奇怪)。
从技术上讲,此块xtemplate
目前仅存在于概念上,因此尚未得到适当覆盖,请参阅下文对缺陷的讨论。您在此处得到的最接近的描述是,\UseInstance
但这不是一个可以独立存在的声明,而是需要上一层使用的东西。
类布局规范
另一个构建块处理特定类的实际实例,例如,如果功能规范说有一个“A-head”和一个“B-head”,那么这里我们就会有
\DeclareTemplateInstance{heading}{A-Head}{some-template}
{key1=val1, key2=val2 ...}
\DeclareTemplateInstance{heading}{B-Head}{maybe-some-other-template}
{key1=val1, key2=val2 ...}
因此,两者共同描述了特定文档类在元素方面提供的内容以及这些元素的格式化方式。
唯一还未解决的是这些元素在实际文档中如何编码,即使用什么语法来输入它们。这有待下一层解决。
用户界面层
顶层将文档输入语法转换为“类设计层(功能规范)”上使用的格式。这方面的一个例子是xparse
提供 LaTeX2e 解析功能的实现,并添加一些额外的功能。但除此之外,还可以提供不同的用户语法,例如一些 xml 接口或...
把这些东西放在一起,利用目前的可能性,我们就会得到类似的东西
\DeclareDocumentCommand \section { * o m } % we implement 2e's interface
{ \UseInstance{heading}{A-head} {#1} {#2} {#2} {#3} }
如果你想知道为什么这看起来这么有趣:为了论证的目的:-)上面假设ObjectType“heading”被定义为需要4个参数:
- 布尔值(指示标题是否不应获取数字
- 目录文本或“NoValue”
- 运行标题的文本或“NoValue”
- 标题文本
由于 2e 文档级接口只有一个可选参数,我们将其复制到 TOC 和 running-head(2e 中也是这样做的),相反,我们可以提供不同的顶级语法,例如
\DeclareDocumentCommand \section { * o m o }
{ \UseInstance{heading}{A-head} {#1} {#2} {#4} {#3} }
它确实利用了对象类型“标题”所具有的所有输入参数(仅顺序不同)
或者 ...
概括
总结一下,这里是对刚刚描述的架构稍微不同的看法(暂时忽略 LDB 部分):
这里再从不同角色的角度来看待这个问题,以及如何使用声明可能性来指定事物xtemplate
:
您可以看到四种不同的角色:
- 这文档类型设计器定义文档类中的元素类型(如果 ObjectType 尚未存在于对象存储库中,则可能定义它们)。他还被列为定义模板接口的人,但我想我标记错了——那可能是设计师做的。
- 如果真的需要制作模板,那么这将是程序员真正理解如何编程
expl3
和所有好东西的人 :-) 但希望对于大多数要求来说,可用的模板都存在(一段时间后) - 的设计师 然后选择合适的模板,并决定其键的正确值类型,如果没有合适的模板,则询问程序员来建造一个。
- 最后还有作者其任务是集中精力撰写真正的文档,而不必担心其他事情
当然,在现实生活中,这些角色最终可能由一个人扮演。
不足之处
... 或者我认为当前原型有什么问题。好吧,当前实现提供的是一种静态设计方法:
- 这是一个抽象对象(类型),比如说一个采用一组定义的输入的标题
- 这里有一堆排版这样的对象的模板
- 因此,要进行设计,选择一个模板,选择正确的参数,就大功告成了
很简单,对吧?其实并不完全如此,因为文档设计通常需要根据上下文灵活处理。以显示标题为例:
- 它们在标题上方有一个定义的空间(例如
preskip
) - 标题下方的空间可能有所不同(
postskip
)
但是如果一个标题直接跟在另一个标题后面,会发生什么情况呢?
\section{A-head}
\subsection{B-head}
它们之间的空间是多少?
- 总和
postskip(A-head)
和preskip(B-head)
? - 两者的最大值?
- 最低?
- 还有别的吗?
LaTeX2e 通过使用\addvspace
实现第二个解决方案(即最大值)轻松解决了这个问题。但这相当限制了设计可能性。
我们可以想到其他可能性,例如解决这个问题,这样标题就可以检测到它直接跟在另一个标题后面,然后使用不同的键,例如between-heading-skip
。但这在概念上是垃圾,因为
- 所有决策都将放在第二个标题中,而不是真正基于 A 标题后面跟着 B 标题的知识
- 它会突然要求模板理解和管理上下文信息,这使它们变得复杂,并且如果单个模板不遵守某些特殊协议,那么所有的识别等都会像纸牌屋一样崩溃。
因此,这是“上下文”的示例,因为有些元素是按顺序排列的。另一个示例是,由于元素位于文档的不同区域,因此它们的行为截然不同。例如
- 标题在前言、正文和后记中的编号不同
- 像 itemize 这样的列表,如果在具有不同字体大小的上下文中使用,或者在特殊区域中使用,则需要完全不同的间距(例如,如果这样的列表出现在边距的方框区域中,您可能需要为它们采用完全不同的设计。
基本上,这些都没有得到适当的支持xtemplate
。
把电影倒回:在 90 年代初期(是的,那是很久以前),我们有一个宏伟的想法,当时我们称之为 LDB,它可以管理高复杂性的上下文依赖关系。从某种程度上说,它是 CSS 的前身(不完全是,但几年后 CSS 提出了一些想法),它可以相当完美地管理所有这些复杂性。然而,当时这些想法只是早了几年,我们最终得出结论,它在技术上行不通。
时代变了:问题仍然没有解决,但计算机的速度越来越快,所以我现在下定决心要重试,见我在印度 TUG 2011 上的演讲[YouTube](毫不奇怪,您会在幻灯片中看到上述的一些图片)。
因此,当时我们实现了 LDB(有两种相互竞争的方法,但这两种方法都太慢,占用太多内存,所以只能搁置了),而是在template.dtx
199x 年提出了“更简单”的方法,后来由 Joseph 重新实现成为
xtemplate.dtx
。对于“静态”设计部分,模板方法确实很好,可以帮到你。但动态部分仍然存在,所以我们尝试以更简单的方式来解决这个问题。
我们曾经有一个想法,那就是提供“集合实例”,基本上就是你可以定义多个实例“集合”,然后在它们之间切换。但这是描述上下文的一种非常不完善和受限的方式,我现在完全相信这是一条死路。所以,请将其视为概念中的“垃圾部分”,这也是我上面没有描述它的原因(但
xtemplate
即使有一些警告,它仍然有记录,所以请注意)。
相反,我认为需要做的是将来自和来自 LDB 的想法汇集在一起template
,以连贯的方式提供它们——现在这是可能的,我相信我对它应该是什么样子有一个很好的愿景。
答案2
这主意是有一个形式化的 key=value 接口来设置印刷布局某些部分的一组参数。
因此,如果你查看文章的目录,你会发现自定义选项与设置一些内部@
命名宏不同
\newcommand\@pnumwidth{1.55em}
\newcommand\@tocrmarg{2.55em}
\newcommand\@dotsep{4.5}
设置顶级计数器
\setcounter{tocdepth}{3}
仅定义内部代码块
\newcommand*\l@section[2]{%
\ifnum \c@tocdepth >\z@
\addpenalty\@secpenalty
\addvspace{1.0em \@plus\p@}%
\setlength\@tempdima{1.5em}%
\begingroup
\parindent \z@ \rightskip \@pnumwidth
\parfillskip -\@pnumwidth
\leavevmode \bfseries
\advance\leftskip\@tempdima
\hskip -\leftskip
#1\nobreak\hfil \nobreak\hb@xt@\@pnumwidth{\hss #2}\par
\endgroup
\fi}
这个想法是,一个更好的接口是文档类可以选择多个基本接口之一模板对于(比如说)目录,它将具有一组记录的参数,可以在标准的无代码声明式接口中设置。
类中的语法可能看起来像
\DeclareInstance { toc } { this-toc} { basic }
{
toclevel = 2,
font = \normalsize,
defaultstyle = dotted,
chapterstyle = undootted,
chapterfont=\large\bfseries,
}
在模板定义中分别声明可能的键并将其映射到实际代码的某处。
有了目录、页面几何、标题、列表等参数化的模板,就有可能实现各种各样的设计师布局规范,而无需真正使用任何内部 TeX 代码。
然而,当前的模板设计和实现已经非常老旧了(我已经 10 年没有看过它了 :-) (显然它的部分比我想象的要新:-)) 并且确实需要重新思考和重新实施,因此虽然想法是合理的,但实际语法不应被认为是稳定的。
答案3
Frank Mittelbach 和 Dave Carlisle 很好地概述了该包背后的想法xtemplate
。在以下 MWE 中,我们希望模拟元素html
。<span>
这通常用于设置内联文本元素的样式。
背后理念的一个好处xtemplate
(在其他答案中可能没有明确体现)是程序员或设计人员可以向用户公开 API。这是开发优秀程序的基本方法。例如,如果 LaTeXe 软件包遵循此概念,我们就可以加载instances
软件包,并且可以减少(甚至完全消除)密钥冲突。
定义模板的过程遵循以下概念面向对象编程。object
首先定义一个,然后创建并使用该对象的实例。一个的理念interface
是它向外部世界公开一个接口。我敢肯定 Frank 在开发这个包的时候使用的是 Java,因为它当时非常流行。每种语言都有自己的特性和模式。正如expl3
成为一种独立的计算机语言一样,这些都是很好的概念。因此,它xtemplate
不仅仅是另一组定义键的宏。
所以我们首先需要定义的是对象。
\DeclareObjectType{inlineobj}{1}
我们给对象起的名字并不重要,但我认为最好使用包的名称,以确保它是唯一的。这只是将参数的数量存储在属性存储中。
该interface
部分编码如下:
\DeclareTemplateInterface{inlineobj}{span}{1}
{
font-face: tokenlist,
font-shape: choice {italic, slanted, normal}=normal,
font-weight: choice {bold, normal}=normal,
font-color: tokenlist=black,
quote: choice {none, enquote}=none,
}
不要对LaTeX3
宏的输入方式感到困惑。这部分只是调用一个带有三个参数的宏\DeclareTemplateInterface {#1}{#2}{#3}
。
接下来我们使用 声明代码 \DeclareTemplateCode{inlineobj}{span}{1}{}{}
。 的\AssignTemplateKeys
开始位置就是代码实现的开始位置。
instance
在我们的例子中,使用类似的代码创建tn
。
\DeclareInstance {inlineobj}{tn}{span}
{
font-face=arial,
font-shape=normal,
font-weight=normal,
font-color=red!80!black,
quote=none,
}
这是不太短的 MWE。
\documentclass{article}
\usepackage{expl3, xtemplate, xparse, csquotes, xcolor, fontspec, xspace}
\newfontfamily\arial{Arial}
\begin{document}
\ExplSyntaxOn
\DeclareObjectType{inlineobj}{1}
\DeclareTemplateInterface{inlineobj}{span}{1}
{
font-face: tokenlist,
font-shape: choice {italic, slanted, normal}=normal,
font-weight: choice {bold, normal}=normal,
font-color: tokenlist=black,
quote: choice {none, enquote}=none,
}
\DeclareTemplateCode{inlineobj}{span}{1}
{
font-face = \l_font_tl,
font-shape = {
italic = \cs_set_nopar:Nn \afontshape: {\itshape},
slanted = \cs_set_nopar:Nn \afontshape: {\itshape},
normal = \cs_set_nopar:Nn \afontshape: {\upshape}
},
font-weight= {
bold = \cs_set_nopar:Nn \afontseries: {\bfseries},
normal =\cs_set_nopar:Nn \afontseries: {\mdseries}
},
font-color = \l_tmpa_tl,
quote = {
none = \cs_set_nopar:Npn \quotemacro:n #1 {\detokenize{#1}},
enquote = \cs_set_nopar:Npn \quotemacro:n #1 {\enquote{\detokenize{#1}}},
unknown = \cs_set_nopar:Npn \quotemacro:n #1 {\detokenize{#1}}
},
}
{
% the implementation part
\AssignTemplateKeys
\group_begin:
\color\l_tmpa_tl
\cs:w \l_font_tl \cs_end:
\afontshape:
\afontseries:\selectfont
\quotemacro:n{#1}
\group_end:
}
\DeclareInstance {inlineobj}{docFunction}{span}
{
font-face=arial,
font-shape=italic,
font-weight=bold,
font-color=green!40!black,
quote=enquote
}
\DeclareDocumentCommand\docFunction { m } {
\IfInstanceExistTF {inlineobj}{docFunction}
{\UseInstance{inlineobj}{docFunction}{#1}}
{ERROR}
}
\DeclareInstance {inlineobj}{tn}{span}
{
font-face=arial,
font-shape=normal,
font-weight=normal,
font-color=red!80!black,
quote=none,
}
\DeclareDocumentCommand\tn{ m }{%
\IfInstanceExistTF {inlineobj}{tn}
{\UseInstance{inlineobj}{tn}{#1}}
{ERROR}
}
\ExplSyntaxOff
The function \docFunction {get_string ( )} is used throughout to get a string in LuaTeX, where macros in text paragraphs are shown as \docFunction{\my_macro} in green. Typewrite text is obtained by using \tn{\tn}.
\end{document}
欢迎对代码进行改进,尤其是\detokenize
删除末尾空格的部分。我还欢迎任何关于如何模拟的想法pgfkeys
处理程序。
答案4
能否用一个简单但完整的示例来描述 xtemplate 的概念?
拖船 33:3有一篇由 Clemens Niederberger 撰写的文章,标题为包xtemplate
:一个例子(现在可供公众访问)。
它需要作者的回答到用于格式化名称(首字母或全名)的宏并以此作为定义“对象”、“接口”和“实例”的实例xtemplate
。作者承认,“xtemplate
对于这个[练习]来说有点过头了”——但这是一个更容易理解的例子。