Skip to content

LaTeX 模板开发最佳实践

Xiangdong Zeng edited this page Nov 8, 2019 · 27 revisions

原则

  1. 模板通常设计为 LaTeX2e 文档类(.cls 文件),用户通过 \documentclass[<options>]{<class>} 加载模板。文档类的基本构成,可以参考 texdoc clsguidetexdoc source2e, sec. 66--68.

  2. 内容与样式分离:文档中的标记应表达语义,控制具体样式的代码应集中定义在 .cls 文件中。
    Liam Huang 的这篇文章介绍了什么是内容与样式分离。

  3. 一致性:模板提供给用户使用的命令(和环境,下同),应和标准文档类、常用宏包和模板的其他命令具有语法和语义上的一致性,详见「一般」一节。

  4. 最小化:模板的主要目的,是为常见文档组件提供一套定制的样式。不要期待模板面面俱到,不常见的文档组件(例如伪代码排版)应由用户通过调用宏包自行实现。可参考 IEEEtran 的做法。

  5. 接口的一致性:尽可能与标准文档类保持一致的接口。

文档类和宏包

  1. 尽量通过调用常见的宏包实现样式定制。

    • 常见的宏包用户多、使用广泛,维护长期且稳定、参考资料丰富,很少出现难以解决的宏包冲突。
  2. 了解所用宏包的依赖项,了解特定宏包组合的加载顺序要求。

  3. 不在模板和示例文档中,重复加载同一个宏包或同一个宏包的组件(例如 tikz library, minted library)。

  4. 集中加载宏包组件,不在模板中零散加载。

  5. 控制宏包使用数量(最小化原则)。

    1. 仅少数用户需要的、宏包加载后配置简单的功能,可考虑不依赖相关宏包。需要时用户自行加载即可。例如排版算法/伪代码、浮动体双语标题、页面水印等功能。如果要在用户调用某宏包后自动进行配置,可以使用 filehookctexhook
    2. 功能重合度较高的宏包,若非必要,不同时使用。例如控制章节标题样式的宏包 ctexheadingtitlesec,提供 if-else 功能的宏包 ifthenetoolbox 等。

一般

  1. 一致性 这里指模板用户命令的一致性。与模板内部实现有关的一致性,可见下方内容。命令的一致性,可以粗略分为语法和语义(包含命名)两部分。

    1. 参照物。衡量一致性时,有两类参照物:
      • 主要的参照物是 latex2e 标准文档类和常用宏包(如 amsmathgeometry 等)。例如,语法上用 [...] 标记可选参数,语义上用 \title\maketitle 标记和生成标题,abstract 环境标记摘要,\chapter\section 分别标记章和节的标题,\frontmatter\mainmatter\backmatter 标记书籍类文档正文部分的起讫等。
      • 次要的参照物是模板自身。
    2. 高复用。复用是内容与样式分离的良好实践,可以降低用户的学习(学习一个新模板的使用方法)和迁移(把 tex 文档从使用模板 A 改为使用模板 B)成本。建议多复用标准文档类提供的通用标记命令。
      • 一些「把 tex 文档转换成其他标记语言」的软件,可能只对标准文档类和常用宏包的命令支持较好,对用户自行定义或拓展部分的支持不佳。
    3. 语法的扩展。根据需要,可合理拓展通用标记命令的语法,或模仿定义新命令。例如,为 abstract 环境增加可选参数、仿照定义 keywords 命令或环境,用于标记关键词。
    4. 语义的拓展。
      • 若非必要,不修改通用标记命令的语义。例如,在不使用 unicode-math 时,常用 \mathbf 标记加粗直立(bold)的数学字符,而用 \bm 标记加粗倾斜(bold italic)的数学字符。慎重、甚至不要修改 \mathbf 的语义(一个讨论例子,见 x-magus/ThesisUESTC/issues/79)。
      • 注意新旧命令的命名一致性。没有唯一的最佳实践,合理且一致即可。例如,需要标记中英双语标题,\title 命令不够用,考虑使用两个命令标记两个标题。使用 \titleCn/\titleEn,或 \setChineseTitle/\setEnglishTitle 都合适,但使用 \title\setChineseTitle 就欠妥。
    5. 文档化。在文档中记录调整过的语法和语义,增改了哪些、它们对应的用法是什么。
  2. 在充分理解既有实现之前,避免直接重定义基础命令,例如 \chapter\l@section

  3. 尽量避免定义新命令以「抽象」基础命令,例如 \newcommand\myChapter[1]{\chapter{附录:#1}}。新命令会增加用户的使用门槛;「抽象」基础命令的这类需求通常都有更好的实现方式,可到社区寻求帮助。

  4. 尽量使用 latex2e 风格的命令,如 counter 相关的、length 相关的、h/vspace 相关的。

    • 若非必要,不使用 tex/plain tex 风格的命令和赋值方式。例如,用 latex2e 的 \newcounter,不用 plain tex 风格的 \newcount
    • 若非必要,不混用两种风格的命令。例如,不混用 \hspace\hskip
  5. 在文档类(.cls 文件)内部,使用 texdoc clsguide 中介绍的命令,例如用 \RequirePackage 载入宏包,不用 \usepackage

  6. 空格的处理

    1. 了解空格的各种产生方式,注意注释源码(例如命令定义)中的多余空格,尤其是换行符产生的空格。
    2. 不滥用 \nobreakspace(不可换行空格),即 ~。例如,使用连续多个 ~ 来插入较宽的空格,就多是滥用;应考虑使用其他方式控制对齐。
  7. 区分带参数与不带参数的命令,区分改变状态与制造输出的命令。

    • 反例:
    • 在不带参数的命令后,使用(额外的、不起任何作用的)大括号,例如 \songti{}出处
    • 把不带参数的命令,当成带参数的命令来使用,例如 \small{text in small size}出处
  8. 了解作用域、分组(grouping)和 \global

    • 建议使用 \begingroup ... 多行内容 ... \endgroup 制造长分组,使用 {...} 制造短分组。
    • 知道 latex 环境自带分组,避免在使用时额外添加分组
    • 防止命令作用域「溢出」,反例:
    • cctart.cls 中在 \figurename 里直接使用 \bf,见一处源码讨论文章
  9. [进阶] 了解错误信息(error/warning/info)的写法,参考 texdoc source2e, sec. 15(latex2e 风格)和 texdoc interface3, part XVII(latex3 风格)。

  10. [进阶] 代码层面的兼容性检测(另见下文条目,文档层面的兼容性描述)

    1. 操作系统的检测,texdoc interface3 , sec. XIV.5 提供了对 unix, windowsunknown 的判断;texdoc ctex, sec. 14.3.6 提供了对 macOS 的判断。
    2. 引擎的检测,可使用 iftex 宏包(latex2e 风格),或 texdoc interface3, sec. XIII.3
    3. 引擎版本的检测,见各引擎的文档,例如 texdoc xetex-reference, sec. 9
    4. 格式版本的检测,参考这个 TeX.SX 回答
    5. 基础文档类和宏包版本的检测,见下一条
    6. 操作系统和引擎的识别和检测,可用于为不同组合设定不同配置。格式、文档类和宏包版本的检测,常用于指定最老兼容版本。
  11. [进阶] 文档类依赖项的最低版本控制

    1. 一个文档类可以依赖一个(基础)文档类和多个宏包。
    2. 加载依赖项时,可通过可选参数指定最低版本(此处的版本,是以 YYYY/MM/DD 格式表示的发布日期)
    \LoadClass[<opions>]{<class>}[YYYY/MM/DD]
    \RequirePackage[<opions>]{<class>}[YYYY/MM/DD]
    1. 用户本地的版本早于指定的最低版本时,编译时会产生 warning。如果觉得 warning 的提示级别不够强,可以使用 texdoc source2e, sec. 68.3 介绍的命令 \@ifpackagelater\@ifclasslater,配合手写错误信息,来产生 error。

源码风格

  1. 如果有大段复制粘贴的代码,建议以注释形式说明来源,方便后续维护。
  2. 使用缩进。保持缩进风格统一,不将空格和 Tab 混用。建议每层缩进使用 2 个或 4 个空格,其中 2 个空格是 latex3 推荐使用的( 见 texdoc l3styleguide

字体

  1. 除非必要,不使用 LaTeX 2.09 风格的字体切换命令,例如 \rm, \bf

  2. 在切换字体之前,推荐使用 \normalfont 把字体恢复到 \rmfamily。类似的命令还有 \normalsize

  3. 谨慎使用 \songti\heiti 等只作用于 CJK 文字的命令,它们不会修改西文字体。推荐使用 \rmfamily, \sffamily 等命令,使西文与中文字体的风格保持一致。

  4. 在了解它们的初始定义之前,不要直接修改 \large, \small 等命令的定义

  5. 修改行距:推荐使用 setspace/zhlineskip 宏包和 \linespread,可以接受修改 \baselinestretch,一定不要直接修改 \baselineskip

  6. \linespread{...}\fontsize{...}{...} 后面一定要跟 \selectfont

  7. 控制示例文档中字体和字号切换命令的使用频次。它们应该隐藏在模板内部,不应该被模板用户(频繁)直接使用。

用户文档

模板应提供用户文档。用户文档的形式自由,推荐包含以下内容

  1. 问题的反馈方式,包括但不限于维护者的联系方式、模板的在线托管地址等。
    建议推荐几个问答社区,方便用户寻求解答。

  2. 文档层面的兼容性描述(另见前文条目,代码层面的兼容性检测)

    1. 适用的操作系统。LaTeX 的跨平台性较好,操作系统主要带来的问题是预装中文字体不同。
    2. 适用的(TeX Live 及其衍生的)发行版版本。例如增加描述「经作者使用和用户反馈,可在 TeX Live 2018 及更新版本上运行。更早的版本没有测试」。MikTeX 发行版可跨年度升级,没有此项问题。
    3. 适用的编译方式,包括引擎和编译选项(例如 -shell-escape
    4. 因不同编辑器的支持情况不同,不建议仅通过写在 tex 文件开头的 magic comment 指定编译方式和选项。建议在用户文档中明确说明。
    5. 如果提供了操作系统相关的辅助编译工具,如 Makefile,应特别说明
  3. 模板定义的新命令的用法,模板修改过的既有命令的新用法或新效果

  4. 使用模板应遵循的文件结构,通常包括子文件的默认路径、图片和其他文件的默认路径等
    文件结构可以在用户文档里通过文字说明,也可以在(随模板一起分发的)示例文档里通过实践体现

  5. 如果使用了 CTAN 未收录的功能性宏包,建议提供获取方式,并在文档中特别说明。可选将这类依赖随模板一起分发

  6. 如果使用了操作系统不预装的字体,建议单独说明。如果该字体是免费可获取的,建议列出获取方式,并提示授权使用范围。

模板的发布和维护

包含完整的模板源码和用户文档的文件或文件包,即是一份模板的发布。如果模板需要长期维护(功能和文档的增删改),模板应该使用版本号、更新日志来记录变化,同时考虑使用版本管理软件,例如 Git。

用 Git 管理 LaTeX 项目时,以下问题值得关注

  • 管理哪些文件
    • LaTeX 辅助文件,不管理。推荐这个适用于 LaTeX 项目的 .gitignore 模板
    • 二进制文件,可以管理,控制更新频次(以控制模板项目的大小)
      • 图片文件,尽可能使用 mwe 宏包提供的示例图片
      • PDF 文件,例如用户文档或示例文档,只在发布新版时更新。如果在 GitHub 托管,可以考虑只管理生成 PDF 的源码,通过 Release 功能提供编译得到的文档。
  • 每次提交,commit message 要言之有物。
    反例:wangxianyu7/sdu_wh_latex/commits, mohuangrui/ucasthesis/commits

其他

  1. 元信息的输入风格

    1. 文档中常需要由用户输入大量元信息。以学位论文为例,可能需要输入的有(双语)标题、作者、指导老师、所在学院、日期等。
    2. 标准文档类的做法是,为每一条信息分配一个命令,例如 \title\author\datebeamer 文档类增加了 \subtitle\institute 等。
    3. 也可以考虑,提供一个统一的设置命令,允许用户使用 key=value 的输入方式,见下方例子。
      • 优势:适当简化输入,处理「初始值、默认值、指定可选值」时更方便;key 中可包含汉字,降低使用门槛
      • 劣势:实现比「一条信息一个命令」稍麻烦;各种实现对选项列表中空格、空行的处理不尽相同,有引发语法错误的风险
      • 常用宏包:l3keys 子包、pgfkeys 宏包
    \setMetaInfo{
      title=xxx, 
      author=xxx,
      key=value,
      键=值
    }