Latex包与类-2: Writing my own class files


本文主要介绍了如何编写Latex中的类(Class)

Latex Project

Latex包与类-2: Writing my own class

有的时候,我们需要高度定制化一个文章出来,那么这个时候最佳的选择就是自己从头开始写一个.cls类文件。本文介绍了一个.cls类文件的主要的结构以及一些必要的命令。

1. Before Writing

在开始编写自己的.cls类文件之前,我们首先需要干两件事以确保自己是不是真的要写一个.cls类文件出来:

  1. CTAN (Comprehensive Tex Archive Network)上看看有没有已经写了并上传一个我们最终目标相似的.cls文件。如果有的话其实我们只需要改改就可以了,这样就可以节省我们大量的时间,因为我们的重点在于组织内容而非格式编排
  2. 根据前一篇文章,确定自己到底是要写一个类文件还是包。因为如果决定错的话,未来做出来的模板的扩展性就很差,而且也会影响到最终成果的美观程度

2. General Structure

一般来说,一个.cls文件需要有以下四个部分/可以被分为以下四个部分:

  1. 定义类(Identification):在根本上.cls.tex.sty文件没什么区别,都是文本文件,因此Latex判断一个文件到底是不是.cls文件是根据特殊的命令的。因此一个.cls文件首先需要通过Latex提供的命令来声明自己是Latex的类文件。
  2. 声明依赖及变量定义(Preliminary Declarations):类文件之间、类文件与包之间会相互引用,因此需要首先声明我们自己写的类文件以来的类和包。此外,一些全局变量的声明也是在这里定义的。
  3. 声明/提供选项(Options):一个类通过提供不同的选项,来实现不同的配置。因此在声明完依赖和变量之后就声明该类提供哪些选项以及该类如何处理这些选项
  4. 更多的定义(More Declarations):类文件的主题

A. Identification

Identification的作用就是让一个类文件声明自己是一个类文件,定义该类文件提供的类。

而进行Identification的语法就两句话

\NeedsTeXFormat{LaTeX2e}[2021/1/28]
\ProvidesClass{exampleclass}[2021/01/28 Example LaTeX class]

其中:

  • \NeedsTexFormat定义了编译这个类需要的Latex编译器版本,后面的[]内的内容是可选选项,表示日期
  • \ProvidesClass定义了这个类文件提供的类,花括号中是提供的类名,而后面的[]中的内容也是日期,可以加上一些说明文字
  • 日期按照yyyy/mm/dd的格式书写

B. Preliminary declarations

一般来说,我们的文章都是需要使用到别的类、包的,因此我们就可以在.cls文件中定义这些依赖

例如下面我们扩充了上面的话

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{exampleclass}[2014/08/16 Example LaTeX class]

\newcommand{\headlinecolor}{\normalcolor}
\LoadClass[twocolumn]{article}
\RequirePackage{xcolor}
\definecolor{slcolor}{HTML}{882B21}

其中:

  • 我们首先通过\newcommand命令自己定义了一个新的命令,后面我们可能会用到这个命令。\newcommand第一个参数是新的命令的名称,第二个参数是调用了新的命令执行的代码。这里的\normalcolor就是恢复到默认颜色。在word中我们设置了前面的字符是某种颜色之后,如果我们不修改的话那么后面的字符也成了相同的颜色,在Latex中也是一样的。
  • 接下来\LoadClass命令加载了article体裁的文章。\LoadClass类似于import#include都是文本意义上的复制粘贴。因此我们这里就表示我们自己的这个类是在article的基础上修改得到的。同样类比于import#include,我们在完成了加载其他类之后,我们就可以使用这个类里面定义的名了。此外我们这里就是在加载article这个class的同时指定了twocolum参数,即双栏文章。
  • 然后,\RequirePackage指明了我们这个类文件以来的包。而\RequirePackage其实除了指明我们用的包以外,还直接帮助我们导入了这个包,因此就和\usepackage一样,我们也可以在花括号{}前使用中括号[]来传入参数
  • 最后,使用\definecolor命令定义了一个颜色缩写,第一个参数是缩写的名称,第二个参数是色彩系统,第三个参数是色彩系统中该色彩具体的值

C. Options

为了增加类的扩展性以适应同一体裁不同的文章的具体要求,我们接下来在类中定义一系列的选项,以及处理具体参数值的语句。

注意,在Latex中,参数是在导入类的时候通过[]来选择的,如果把整个class当做一个函数的话,这里有点类似于我们这里定义函数的参数,而在.tex导入类的时候则类比于传入具体的参数值

我们下面就添加了更多的语句

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{exampleclass}[2014/08/16 Example LaTeX class]

\newcommand{\headlinecolor}{\normalcolor}
\RequirePackage{xcolor}
\definecolor{slcolor}{HTML}{882B21}

\DeclareOption{onecolumn}{\OptionNotUsed}
\DeclareOption{green}{\renewcommand{\headlinecolor}{\color{green}}}
\DeclareOption{red}{\renewcommand{\headlinecolor}{\color{slcolor}}}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions\relax
\LoadClass[twocolumn]{article}

其中:

  • \DeclareOption命令接受两个必须的参数,第一个参数是该选项的名称,而第二个参数是若指定该参数/给参数传入数值后需要执行的命令
    • \OptionNotUsed会在日志里输出一句话,表示这个选项不能使用。这样的话当用户在使用\OptionNotUsed这个参数的时候就会被忽略掉,这样就强制文章是双栏的,这样做是为了覆盖掉基类的选项
    • \renewcommand用于重新定义一个已经定义过的命令,这里就是说如果传入了green选项的话,那么就把标题的颜色设置为绿色,如果传入了red选项的话,那么就使用上面定义的红色
    • \DeclareOption*{}则表示没有传入参数时候执行的代码,因此只需要一个参数,即执行的命令即可
    • \PassOptionsToClass{options-list}{package-name}用于将optons-list参数中列出的选项(不止一个时用逗号隔开)传递给包package-name。即执行了package-name中这些option对应的代码,相当于在\RequirePackage中指定/传入了option-list中的选项。
    • \CurrentOption表示当前所有的选项
  • 注意,前面\DeclareOption都只是声明了选项和如果指定该选项执行的代码,而\ProcessOptions\relax这一句话才表示真正的处理传入的参数,找到对应的定义,执行代码。所以这一句话必须在所有的\DeclaraOption后面

D. More declarations

在声明完选项之后,我们就可以开始对文章中的细节进行设置了,包括行间距、标题字号等等。例如

\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{exampleclass}[2014/08/16 Example LaTeX class]

\newcommand{\headlinecolor}{\normalcolor}
\RequirePackage{xcolor}
\definecolor{slcolor}{HTML}{882B21}

\DeclareOption{onecolumn}{\OptionNotUsed}
\DeclareOption{green}{\renewcommand{\headlinecolor}{\color{green}}}
\DeclareOption{red}{\renewcommand{\headlinecolor}{\color{slcolor}}}
\DeclareOption*{\PassOptionsToClass{\CurrentOption}{article}}
\ProcessOptions\relax
\LoadClass[twocolumn]{article}

\renewcommand{\maketitle}{%
    \twocolumn[%
        \fontsize{50}{60}\fontfamily{phv}\fontseries{b}%
        \fontshape{sl}\selectfont\headlinecolor
        \@title
        \medskip
        ]%
}

\renewcommand{\section}{%
    \@startsection
    {section}{1}{0pt}{-1.5ex plus -1ex minus -.2ex}%
    {1ex plus .2ex}{\large\sffamily\slshape\headlinecolor}%
}

\renewcommand{\normalsize}{\fontsize{9}{10}\selectfont}
\setlength{\textwidth}{17.5cm}
\setlength{\textheight}{22cm}
\setcounter{secnumdepth}{0}

而这些内容都涉及到Latex具体细节的设置了,例如:字号、字体等等。因此这里就不展开了。

然而More Declaration这一部分才是一个.cls文件关键的地方。真正决定长什么样子的地方就在这里,因此我们要学习怎么学.cls文件的编写,另外一个重要的内容就是学习如何写More Declaration。

而学习的方法只有一个,就是看别人是怎么写的,从他们写的.cls中来学习

3. Handling errors

因为我们的类中也会执行代码,因此也会存在代码运行出错的可能,此时我们就需要去处理错误。而Latex并不完全是一个编程语言,因此不存操作try……except……、catch……throw……这类异常处理机制。

为此,Latex中的处理错误的机制其实就是抛出异常,然后让写文章的人自己根据日志来进行修改,因此常用的在日志中报错的命令有:

  • \ClassError{*class-name*}{*error-text*}{*help-text*}:Error直接中止编译,对应Overleaf中的红色。此外三个参数分别是报错的时候说哪一个类在报错,保存的信息,提供的帮助信息
  • \ClassWarning{*class-name*}{*warning-text*}:Warning只会警告,但是编译过程并不会停下来,对应Overleaf中的黄色。参数和Error类似
  • \ClassWarningNoLine{*class-name*}{*warning-text*}:NoLine的Warning效果和上面的warning一样,但是不会报行号
  • \ClassInfo{*class name*}{*info-text*}:打印出来信息就结束了,对应Overleaf中的蓝色

4. Commonly used commands

下面列出来了在class中常用的一些命令

  • \newcommand{*name*}{*definition*}:上面介绍过了,略。参考new command,其中有更深入的讲解
  • \renewcommand{}{}:同样说过了
  • \providecommand{}{}:和\newcommand一样的效果,但是如果命令已经存在,那么这个命令不会覆盖之前的命令,而知直接被无视掉
  • \CheckCommand{}{}:语法和\newcommand一样,但是会先检查一下这个命令是否存在,以及如果存在了时候和\CheckCommand{}{}中提供的定义有一样。不一样的话就会产生一个warning。
  • \setlength{}{}:第一个参数是要展示的内容,第二个参数是长度
  • \mbox{}:创建一个装着{}里内容的盒子。
  • \fbox{}:和\mbox{}一样,但是这个盒子的位置会浮动。效果则类似于\begin{figure}环境时候会根据内容自动寻找合适的位置

文章作者: Jack Wang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jack Wang !
  目录