-
Notifications
You must be signed in to change notification settings - Fork 400
LaTeX 模板开发最佳实践
-
模板通常设计为 LaTeX2e 文档类(
.cls
文件),用户通过\documentclass[<options>]{<class>}
加载模板。文档类的基本构成,可以参考texdoc clsguide
和texdoc source2e
, sec. 66--68. -
内容与样式分离:文档中的标记应表达语义,控制具体样式的代码应集中定义在
.cls
文件中。
Liam Huang 的这篇文章介绍了什么是内容与样式分离。 -
一致性:模板提供给用户使用的命令(和环境,下同),应和标准文档类、常用宏包和模板的其他命令具有语法和语义上的一致性,详见「一般」一节。
-
最小化:模板的主要目的,是为常见文档组件提供一套定制的样式。不要期待模板面面俱到,不常见的文档组件(例如伪代码排版)应由用户通过调用宏包自行实现。可参考
IEEEtran
的做法。 -
接口的一致性:尽可能与标准文档类保持一致的接口。
-
尽量通过调用常见的宏包实现样式定制。
- 常见的宏包用户多、使用广泛,维护长期且稳定、参考资料丰富,很少出现难以解决的宏包冲突。
-
了解所用宏包的依赖项,了解特定宏包组合的加载顺序要求。
-
不在模板和示例文档中,重复加载同一个宏包或同一个宏包的组件(例如 tikz library, minted library)。
-
集中加载宏包组件,不在模板中零散加载。
-
控制宏包使用数量(最小化原则)。
- 仅少数用户需要的、宏包加载后配置简单的功能,可考虑不依赖相关宏包。需要时用户自行加载即可。例如排版算法/伪代码、浮动体双语标题、页面水印等功能。如果要在用户调用某宏包后自动进行配置,可以使用
filehook
或ctexhook
。 - 功能重合度较高的宏包,若非必要,不同时使用。例如控制章节标题样式的宏包
ctexheading
和titlesec
,提供 if-else 功能的宏包ifthen
和etoolbox
等。
- 仅少数用户需要的、宏包加载后配置简单的功能,可考虑不依赖相关宏包。需要时用户自行加载即可。例如排版算法/伪代码、浮动体双语标题、页面水印等功能。如果要在用户调用某宏包后自动进行配置,可以使用
-
一致性 这里指模板用户命令的一致性。与模板内部实现有关的一致性,可见下方内容。命令的一致性,可以粗略分为语法和语义(包含命名)两部分。
- 参照物。衡量一致性时,有两类参照物:
- 主要的参照物是 latex2e 标准文档类和常用宏包(如
amsmath
、geometry
等)。例如,语法上用[...]
标记可选参数,语义上用\title
和\maketitle
标记和生成标题,abstract
环境标记摘要,\chapter
和\section
分别标记章和节的标题,\frontmatter
、\mainmatter
和\backmatter
标记书籍类文档正文部分的起讫等。 - 次要的参照物是模板自身。
- 主要的参照物是 latex2e 标准文档类和常用宏包(如
- 高复用。复用是内容与样式分离的良好实践,可以降低用户的学习(学习一个新模板的使用方法)和迁移(把 tex 文档从使用模板 A 改为使用模板 B)成本。建议多复用标准文档类提供的通用标记命令。
- 一些「把 tex 文档转换成其他标记语言」的软件,可能只对标准文档类和常用宏包的命令支持较好,对用户自行定义或拓展部分的支持不佳。
- 语法的扩展。根据需要,可合理拓展通用标记命令的语法,或模仿定义新命令。例如,为
abstract
环境增加可选参数、仿照定义keywords
命令或环境,用于标记关键词。 - 语义的拓展。
- 若非必要,不修改通用标记命令的语义。例如,在不使用
unicode-math
时,常用\mathbf
标记加粗直立(bold)的数学字符,而用\bm
标记加粗倾斜(bold italic)的数学字符。慎重、甚至不要修改\mathbf
的语义(一个讨论例子,见 x-magus/ThesisUESTC/issues/79)。 - 注意新旧命令的命名一致性。没有唯一的最佳实践,合理且一致即可。例如,需要标记中英双语标题,
\title
命令不够用,考虑使用两个命令标记两个标题。使用\titleCn/\titleEn
,或\setChineseTitle/\setEnglishTitle
都合适,但使用\title
和\setChineseTitle
就欠妥。
- 若非必要,不修改通用标记命令的语义。例如,在不使用
- 文档化。在文档中记录调整过的语法和语义,增改了哪些、它们对应的用法是什么。
- 参照物。衡量一致性时,有两类参照物:
-
在充分理解既有实现之前,避免直接重定义基础命令,例如
\chapter
、\l@section
。 -
尽量避免定义新命令以「抽象」基础命令,例如
\newcommand\myChapter[1]{\chapter{附录:#1}}
。新命令会增加用户的使用门槛;「抽象」基础命令的这类需求通常都有更好的实现方式,可到社区寻求帮助。 -
尽量使用 latex2e 风格的命令,如 counter 相关的、length 相关的、h/vspace 相关的。
- 若非必要,不使用 tex/plain tex 风格的命令和赋值方式。例如,用 latex2e 的
\newcounter
,不用 plain tex 风格的\newcount
- 若非必要,不混用两种风格的命令。例如,不混用
\hspace
和\hskip
。
- 若非必要,不使用 tex/plain tex 风格的命令和赋值方式。例如,用 latex2e 的
-
在文档类(
.cls
文件)内部,使用texdoc clsguide
中介绍的命令,例如用\RequirePackage
载入宏包,不用\usepackage
。 -
空格的处理
- 了解空格的各种产生方式,注意注释源码(例如命令定义)中的多余空格,尤其是换行符产生的空格。
- 不滥用
\nobreakspace
(不可换行空格),即~
。例如,使用连续多个~
来插入较宽的空格,就多是滥用;应考虑使用其他方式控制对齐。
-
区分带参数与不带参数的命令,区分改变状态与制造输出的命令。
-
了解作用域、分组(grouping)和
\global
-
[进阶] 了解错误信息(error/warning/info)的写法,参考
texdoc source2e
, sec. 15(latex2e 风格)和texdoc interface3
, part XVII(latex3 风格)。 -
[进阶] 代码层面的兼容性检测(另见下文条目,文档层面的兼容性描述)
- 操作系统的检测,
texdoc interface3
, sec. XIV.5 提供了对unix
,windows
和unknown
的判断;texdoc ctex
, sec. 14.3.6 提供了对macOS
的判断。 - 引擎的检测,可使用
iftex
宏包(latex2e 风格),或texdoc interface3
, sec. XIII.3 - 引擎版本的检测,见各引擎的文档,例如
texdoc xetex-reference
, sec. 9 - 格式版本的检测,参考这个 TeX.SX 回答
- 基础文档类和宏包版本的检测,见下一条
- 操作系统和引擎的识别和检测,可用于为不同组合设定不同配置。格式、文档类和宏包版本的检测,常用于指定最老兼容版本。
- 操作系统的检测,
-
[进阶] 文档类依赖项的最低版本控制
- 一个文档类可以依赖一个(基础)文档类和多个宏包。
- 加载依赖项时,可通过可选参数指定最低版本(此处的版本,是以
YYYY/MM/DD
格式表示的发布日期)
\LoadClass[<opions>]{<class>}[YYYY/MM/DD] \RequirePackage[<opions>]{<class>}[YYYY/MM/DD]
- 用户本地的版本早于指定的最低版本时,编译时会产生 warning。如果觉得 warning 的提示级别不够强,可以使用
texdoc source2e
, sec. 68.3 介绍的命令\@ifpackagelater
和\@ifclasslater
,配合手写错误信息,来产生 error。
- 如果有大段复制粘贴的代码,建议以注释形式说明来源,方便后续维护。
- 使用缩进。保持缩进风格统一,不将空格和 Tab 混用。建议每层缩进使用 2 个或 4 个空格,其中 2 个空格是 latex3 推荐使用的( 见
texdoc l3styleguide
)
-
除非必要,不使用 LaTeX 2.09 风格的字体切换命令,例如
\rm
,\bf
等 -
在切换字体之前,推荐使用
\normalfont
把字体恢复到\rmfamily
。类似的命令还有\normalsize
-
谨慎使用
\songti
、\heiti
等只作用于 CJK 文字的命令,它们不会修改西文字体。推荐使用\rmfamily
,\sffamily
等命令,使西文与中文字体的风格保持一致。 -
在了解它们的初始定义之前,不要直接修改
\large
,\small
等命令的定义 -
修改行距:推荐使用
setspace/zhlineskip
宏包和\linespread
,可以接受修改\baselinestretch
,一定不要直接修改\baselineskip
-
\linespread{...}
和\fontsize{...}{...}
后面一定要跟\selectfont
-
控制示例文档中字体和字号切换命令的使用频次。它们应该隐藏在模板内部,不应该被模板用户(频繁)直接使用。
模板应提供用户文档。用户文档的形式自由,推荐包含以下内容
-
问题的反馈方式,包括但不限于维护者的联系方式、模板的在线托管地址等。
建议推荐几个问答社区,方便用户寻求解答。 -
文档层面的兼容性描述(另见前文条目,代码层面的兼容性检测)
- 适用的操作系统。LaTeX 的跨平台性较好,操作系统主要带来的问题是预装中文字体不同。
- 适用的(TeX Live 及其衍生的)发行版版本。例如增加描述「经作者使用和用户反馈,可在 TeX Live 2018 及更新版本上运行。更早的版本没有测试」。MikTeX 发行版可跨年度升级,没有此项问题。
- 适用的编译方式,包括引擎和编译选项(例如
-shell-escape
) - 因不同编辑器的支持情况不同,不建议仅通过写在
tex
文件开头的 magic comment 指定编译方式和选项。建议在用户文档中明确说明。 - 如果提供了操作系统相关的辅助编译工具,如
Makefile
,应特别说明
-
模板定义的新命令的用法,模板修改过的既有命令的新用法或新效果
-
使用模板应遵循的文件结构,通常包括子文件的默认路径、图片和其他文件的默认路径等
文件结构可以在用户文档里通过文字说明,也可以在(随模板一起分发的)示例文档里通过实践体现 -
如果使用了 CTAN 未收录的功能性宏包,建议提供获取方式,并在文档中特别说明。可选将这类依赖随模板一起分发
-
如果使用了操作系统不预装的字体,建议单独说明。如果该字体是免费可获取的,建议列出获取方式,并提示授权使用范围。
包含完整的模板源码和用户文档的文件或文件包,即是一份模板的发布。如果模板需要长期维护(功能和文档的增删改),模板应该使用版本号、更新日志来记录变化,同时考虑使用版本管理软件,例如 Git。
用 Git 管理 LaTeX 项目时,以下问题值得关注
- 管理哪些文件
- LaTeX 辅助文件,不管理。推荐这个适用于 LaTeX 项目的
.gitignore
模板 - 二进制文件,可以管理,控制更新频次(以控制模板项目的大小)
- 图片文件,尽可能使用
mwe
宏包提供的示例图片 - PDF 文件,例如用户文档或示例文档,只在发布新版时更新。如果在 GitHub 托管,可以考虑只管理生成 PDF 的源码,通过 Release 功能提供编译得到的文档。
- 图片文件,尽可能使用
- LaTeX 辅助文件,不管理。推荐这个适用于 LaTeX 项目的
- 每次提交,commit message 要言之有物。
反例:wangxianyu7/sdu_wh_latex/commits, mohuangrui/ucasthesis/commits
-
元信息的输入风格
- 文档中常需要由用户输入大量元信息。以学位论文为例,可能需要输入的有(双语)标题、作者、指导老师、所在学院、日期等。
- 标准文档类的做法是,为每一条信息分配一个命令,例如
\title
、\author
和\date
。beamer
文档类增加了\subtitle
和\institute
等。 - 也可以考虑,提供一个统一的设置命令,允许用户使用
key=value
的输入方式,见下方例子。- 优势:适当简化输入,处理「初始值、默认值、指定可选值」时更方便;
key
中可包含汉字,降低使用门槛 - 劣势:实现比「一条信息一个命令」稍麻烦;各种实现对选项列表中空格、空行的处理不尽相同,有引发语法错误的风险
- 常用宏包:
l3keys
子包、pgfkeys
宏包
- 优势:适当简化输入,处理「初始值、默认值、指定可选值」时更方便;
\setMetaInfo{ title=xxx, author=xxx, key=value, 键=值 }