ConTeXt
What is ConTeXt#
时常见到有人问“什么是 ConTeXt”,“ConTeXt 和 LaTeX 是什么关系”,还有“我学过 LaTeX 了,还要学 ConTeXt 么”。我的理解是,LaTeX 是用来写文章的,ConTeXt 是用来排版的。LaTeX 有一套非常稳重的默认风格,写论文很方便。而且平常能见到的 LaTeX 文档大多都是使用默认风格或者只做很少的修改写成的。ConTeXt 的默认风格很垃圾,使用时需要改很多东西才能有比较好的效果。但是修改 ConTeXt 的选项非常方便,它有一套统一的界面用来修改所有的设置。LaTeX 则需要使用很多宏包,各个宏包的界面都不一样。
关于 ConTeXt 的文档#
ConTeXt 的官方 reference manual 已经很旧了(因为 ConTeXt 更新很快),现在 Hans (也就是发明 ConTeXt 的人)等人正在重写,但是进度貌似比较慢。如果你需要查询 ConTeXt 的某个选项,最好的方法是去它的官方 wiki,或者搜索它的邮件列表历史。当然下载一份旧版的 reference manual 备用也是不错的。另外 ConTeXt 有很多非常强悍而诡异的功能(比如 fake text,MathML…)被记录在一些零星的小文档或者幻灯片中,这些文档可以在 Hans 公司的网站上找到。除了可以获取信息以外,它们都排版的非常漂亮,很有参考价值。
在阅读 ConTeXt 的文档时,经常可以看到两个词: MKII 和 MKIV。这是 ConTeXt 的两个版本。MKII 是使用 PDFTeX 或 XeTeX 作为编译器的版本,已经过时了;MKIV 是现在一般使用的版本,使用 LuaTeX 作为编译器。
使用 ConTeXt#
关于发行版的选择#
和 LaTeX 一样,ConTeXt 有很多发行版。比如 TeXLive 里就带了 MKII 和 MKIV。但是由于 ConTeXt 更新很快,一般推荐使用 ConTeXt Minimals。安装过程请见这里。
ConTeXt 的设置界面#
ConTeXt 有一个统一的设置界面:
\setupblabla[switch1, switch2, ...]...[var1=value1, var2=value2, ...]
如果是要定义一个新的东西,就是
\defineblabla[name][switch1, switch2, ...]...[var1=value1, var2=value2, ...]
ConTeXt 鼓励用户定义自己的样式。比如默认的列表是这样的
\startitemize
\item Pink Floyd
\item Muse
\stopitemize
但是真正使用时一般都会定义自己的列表类型
\defineitemgroup[mylist][levels=1]
实际上默认的 itemize
列表只是预定义的一种 itemgroup
。这时我们就可以设置 mylist
这种列表
\setupitemgroup[mylist][1][n, packed]
然后在文档中我们就可以这样使用 mylist
:
\startmylist
\item Pink Floyd
\item Muse
\stopmylist
常用选项和环境#
ConTeXt 的宏和环境浩如烟海,浩到官方文档都写不全…这里只讲解一些曾经让我困惑良久,现在还继续让我困惑的。
Text
环境#
Text
环境就像 LaTeX 中的 document
环境,所有会在文档中显示出来的东西都被包含在 \starttext
和 \stoptext
中。而设置一般都放在 \starttext
之前。
页面设置#
ConTeXt 使用 \setuppapersize
设置页面大小
\setuppapersize[A4][A4]
当两个选项的尺寸不一样时,可以实现把很多页印在一张大纸上,方便印刷。另外 ConTeXt 还提供一个 S
系列尺寸,宽高比是 4:3,用来排幻灯片,一般的幻灯片都用 =S6=。在每个尺寸之后还可以再加一个选项,详情请见 Wiki。
在 ConTeXt 中,一个页面从上到下共有七个尺寸,分别是 topspace
, header
, headerdistance
, height, footerdistance
, footer
, 和 bottomspace
。其中 topspace
是纸张上沿到 header 上沿的距离, header
是 header 的高度,headerdistance
是 header 下沿到正文上沿的距离, height
是 header 上沿到footer 下沿的距离。Footer 版本以此类推。从左到右也有类似的七个尺寸,分别是 leftedge
, leftmargin
, leftmargindistance
, width, rightmargindistance
, rightmargin
, rightedge
,注意 width 是正文的宽度。另外再加一个 backspace
,是纸张内沿(就是靠近书脊的那个边缘)到正文内沿的距离。如果你看看 Wiki 中的 Layout 条目,就会发现其实还有很多很多的尺寸,比如与 topspace
类似的还有 topdistance
和 top
,具体区别貌似与 oversize print 等印刷问题有关。
我一般对尺寸没有很严格的要求,所以设置竖直方向的尺寸时,我一般设置 height
为 fit,(表示根据其他尺寸自动调整,以填满整个页面的高度为准。)然后调整其他六个尺寸,如果要求再松一些,我就只调 topspace
和 bottomspace
两个尺寸,其他四个尺寸直接使用默认值。设置水平方向时,如果是单面打印,就设置 width
为 middle
,然后只调整 backspace
,这样 ConTeXt 就会把正文放在中间,两边到纸张边缘的距离都是 backspace
;如果是双面打印,就设置 backspace
和 width
,然后根据情况设置 margin 的参数。所以我的单面文档一般就是这样:
\setuppapersize[letter][letter]
\setuplayout[backspace=2.5cm, width=middle,
height=fit, topspace=2cm, bottomspace=3cm]
注意 2.5cm 的 backspace
是比较小的,排有长公式的文档比较方便,一般的文档可以设为 3.5cm 或 4cm。
如果是排双面文档,会稍微复杂些
\setuplayout[
location=doublesided, % This line seems to be optional.
backspace=.111\paperwidth,
width=.6666\paperwidth,
height=.8176\paperheight,
leftmargin=0.06\paperwidth,
rightmargin=.15\paperwidth,
topspace=.0555\paperheight,
header=.0555\paperheight,
footer=.0555\paperheight,
headerdistance=0pt,
footerdistance=0.04\paperheight,
]
这个设置使用了经典的 Van de Graaf canon。排双面文档时还要注意两点:
- 与一般人的想象不同(好吧,应该说与我一开始的想象不同…),双面文档的正文是靠里的,内侧的距离比较小
- 要想让双面排版生效(就是出那种奇数页与偶数页对称的效果),还要设置一下
pagenumbering
\setuppagenumbering[alternative=doublesided]
在调整页面设置的时候,我们可以使用 ConTeXt 的一大特色—— visual debugging。这个功能博大精深,这里讲解在调整页面时比较有用的几个宏。在使用 visual debugging 之前,先要包含它的模块 m-visual
\usemodule[m-visual]
第一个宏是 \showframe
,把这个宏放在 \starttext
之前的任意位置,在编译后页面的各个部分的边框就会清晰地显示在文档里。第二个是 \showlayout
,这个宏在 \showframe
的基础上在文档的最前面列出与页面设置有关的所有变量的值,包括前面说过的 topspace
, backspace
等等。最后是传说中的 fake text。很多时候我们需要在开始写文档之前就定好页面设置。这时的文档就是白纸一张,即使用 \showframe
看上去也比较别扭,我们需要填充一些文字来预览效果。当然,我们可以到网上随机粘贴一段文字过来,但是 ConTeXt 的方法是随机生成一些黑方块来假装单词。比如
\fakewords{100}{200}
的作用是产生一段黑方块假装单词,单词的个数在 100 到 200 之间。Fake text 这个功能被记录在 Faking Text and More 这个文档里。ConTeXt 除了能假装文字之外还能假装图表和公式😑;…
列表#
ConTeXt 里的列表样式非常多,所以还是用一个例子来说明。我在写作业时希望整个文档是一个大列表,每个大题是一个 \item
,其中可以有多个小题,每个大题中的小题是一个子列表。另外大题的题号是阿拉伯数字,放在左 margin 里,小题的题号是小写字母,就放在正文左边。
首先定义我们自己的 item group,名叫 problem
,可以有两层:
\defineitemgroup[problem][levels=2]
设置大题的样式
\setupitemgroup[problem][1][n, inmargin]
\setupitemgroup[problem][1][stopper=., inbetween={\blank[3em]}]
那个 [1]
表示设置第一层的样式, n
表示使用阿拉伯数字, stopper
是显示在题号后面的东西,最后在大题之间加入 3em 的空白。注意在 ConTeXt 里switches 和 variables 的设置貌似不能写在一起,也就是说,上面两行*不能*合成这样的一行:
\setupitemgroup[problem][1][n, inmargin, stopper=., inbetween={\blank[3em]}]
接下来设置小题的样式
\setupitemgroup[problem][2][a, standard]
\setupitemgroup[problem][2][left=(, right=), stopper=]
第一行的 a
表示使用小写字母作为题号。第二行在题号的两边加小括号。
最后我们可以这样使用新定义的列表:
\startproblem
\item This is problem 1.
\startproblem
\item This is question 1(a). Since $2$ is defined as the next
number of $1$, it is trivial that $1 + 1 = 2$.
\item This is question 1(b). Since $1 + 1 = 2$, it is trivial that
$1 + 1 \neq 3$.
\stopproblem
\item This is problem 2, which is easy.
\stopproblem
注意我们第二次使用 \startproblem
的时候,列表自动进入第二层。ConTeXt 列表的详细用法请看官方文档和 Wiki。
字体#
ConTeXt 中的字体设置是生命中不能承受之复杂,同时也是生命中不能承受之强大。在新版的官方文档中至少会有两章完整的描述字体机制。在 ConTeXt 中,常用的(仅仅是常用的而已…)字体有三个 styles: serif, sans serif 和 teletype (monospaced);每个 style 里有多个 alternatives,比如 roman, italic, bold, slanted 和 smallcap。注意除了 slanted 一般就是 roman 变一下形以外,其他 alternatives 都是单独设计的。除此之外,有些字体(比如自带的 Computer Modern 和 Latin Modern)在每个 alternatives 里还包含不同的尺寸,每种尺寸的形状略有差别。
字体的详细说明请见新版官方文档中的 typography 章和 font 章。
字体切换#
ConTeXt 使用 Plain-TeX 形式的宏来切换字体。比如三个 styles 对应的宏是 \rm
, \ss
和 \tt
;上面提到的五个 alternatives 的宏分别是 \tf
, \it
, \bf
, \sl
和 \sc
。这两组宏可以组合起来,比如 \rmit
选择 italic 的 serif 字体,\ssbf
选择 bold 的 sans serif 字体。宏 \rm
比较特殊,一般使用 \rm
就表示既 serif 又 roman。
ConTeXt 还提供一组宏方便的切换字号,以 1.2 为比例,以一个 x
结尾的宏是小号,以 xx
结尾的宏是超小号,以 a
, b
, c
, d
结尾的宏分别是大号,巨大号,超巨大号,和生命中不能承受之大号。比如,如果正文的字号是 12pt,那么`\rmx` 是 10pt serif roman,\tta
是 14.4pt monospaced。如果只想改变字号不想改变 style 和 alternatives,可以使用 t
系列宏: \tx
, \txx
, \ta
…
Font features#
一个字体文件中往往有很多附加的字符,比如 fi 的连写,比例数(比如½)等等,这些东西称为 features。ConTeXt 可以通过定义 feature sets 选择性的使用这些 features,比如可以定一个 feature set 叫 fancy
\definefontfeature
[fancy]
[language=dflt, script=latn, pnum=yes, mode=node,
onum=yes, kern=yes, liga=yes, dlig=no, zero=no,
protrusion=quality, expansion=quality]
其中几个比较重要的 features 是
mode
: 在 MKIV 中一般就设为node
.pnum
: 一般来说一个字体中的数字的宽度都是一样的,打开pnum
以后宽度就会不一样,比如 1 可能就要比 0 窄一点。onum
: 小写数字,又称老式数字,就是数字都不一样高的那种。kern
: Kerning,打开就是了。liga
: 普通的连写。fi, fl 这些。tlig
: TeX 连写,不知道和liga
有什么区别。trep
: TeX 的引号支持,就是用\
`` 和’'` 来排版双引号的那个。smcp
: Smallcap
另外 protrusion
和 expansion
是 ConTeXt 中的两个微排版算法,具体用法请看官方文档。
ConTeXt 预定义了三个 feature sets,分别是
default
:liga
+kern
+tlig
+trep
smallcaps
:default
+smcp
oldstyle
:default
+onum
字体定义#
在 ConTeXt 中可以使用 \definefontsynonym
来给一个字体定义一个别名。这个宏有三个参数,第一个是要定义的别名,第二个是字体的名字,第三个选项列表。比如
\definefontsynonym[Serif] [Palatino][features=default]
\definefontsynonym[SmallCap][Palatino][features=smallcaps]
定义了两个别名: Serif
和 SmallCap
。这两个名字都指向一个叫做 Palatino 的字体,但是 SmallCap
使用了 smallcaps
feature set。所以使用 Serif
这个名字的时候出来的普通的 Palatino,使用 SmallCap
这个名字的时候出来的就是 smallcap 版的 Palatino。注意这个宏的第二个参数也可以是一个别名。
MKIV 的编译器 LuaTeX 可以使用操作系统中安装的 TrueType 和 OpenType 字体文件,这时 \definefontsynonym
的第二个参数可以以 name:
或 file:
开头来使用这些字体文件。 name:
后接的是字体的名字去掉空格,字体的名字可以通过 mtxrun --script fonts
来查看,具体用法请看 Wiki 中的 Font in LuaTeX 条目。 file:
后接的是字体的文件名去掉扩展名。
ConTeXt 中还可以使用 \definefont
来定义某种字体的宏。比如
\definefont[FancyFont][Serif]
定义了一个宏 \FancyFont
用来使用一个名(或别名)叫 Serif
的字体。第二个参数中也可以使用 TeX 标准的 at
和 scaled
来引用某个尺寸的字体。比如
\definefont[FancyFont][Serif at 72pt]
详情请见新版官方文档的 fonts 章。
前面提到字体可以有 n 多种 styles 和 m 多种 alternatives。要定义全部这些字体需要行字体定义。为了方便字体定义的重用,ConTeXt 提供一个typescript 机制来组织字体定义。一个 typescript 定义可以包含多个 \definefontsynonym
和 \definefont
。当一个定义好的 typescript 被引用的时候,里面的字体定义才会生效。这样就可以在一个文件里定义很多个 typescripts,然后在写文档的时候包含这个文件,简单的引用几个 typescripts,就可以完成多个字体的定义。比如
\starttypescript[myFonts]
\definefontsynonym[Serif][Palatino]
\definefontsynonym[Sans][Optima]
\stoptypescript
然后使用这个 typescripts
\usetypescript[myFonts]
就可以完成 Serif
和 Sans
两个字体的定义。
实际使用时,我们不会使用 \definefont
来定义字体,而是使用 \definetypeface
。这个宏与 typescript 的关系异常暧昧。简单地说,这个宏的定义里包含数个 \usetypescript
,并且它的第三和第四个参数与 \usetypescript
的第一和第二个参数分别匹配,而 \usetypescript
的参数又与 \starttypescript
的参数匹配。更复杂的字体定义工作还会使用到第五个参数。比如,现在假定我们定义 Palatino 的 typescript 如下
\starttypescript[serif][myPalatino]
\definefontsynonym[palarm][name:PalatinoLTStd-Roman]
\definefontsynonym[palait][name:PalatinoLTStd-Italic]
\definefontsynonym[palabf][name:PalatinoLTStd-Bold]
\definefontsynonym[palabi][name:PalatinoLTStd-BoldItalic]
\definefontsynonym[Serif] [palarm][features=fancy]
\definefontsynonym[SerifItalic] [palait][features=fancy]
\definefontsynonym[SerifBold] [palabf][features=fancy]
\definefontsynonym[SerifBoldItalic][palabi][features=fancy]
\definefontsynonym[SerifCaps] [Serif] [features=smallcaps]
\stoptypescript
然后把 \definetypeface
包含在另一个 typescript 里
\starttypescript[NiuBiFonts]
\definetypeface[NiuBiFonts][rm][serif][myPalatino][default]
\stoptypescript
然后把正文字体设定为新定义的 NiuBiFonts
\usetypescript[NiuBiFonts]
\setupbodyfont[NiuBiFonts, 12pt]
注意在使用 NiuBiFonts
这个 typescript 的时候会自动执行那个 \definetypeface
,而由于 \definetypeface
内部的 \usetypescript
,它又会使用 myPalatino
这个 typescript,于是就完成了一大坨字体的定义。另外注意 myPalatino
里面对 Serif
, SerifItalic
等一票字体的定义,最终会自动变成 \rm
, \rmit
等等。所有这些暧昧关系的结果就是,我们可以把所有的字体和常用字体组合都直观地定义在一个文件里,在写文档的时候只要直接使用一个字体组合,就可以为这个文档定义好所有的字体。比如可以定义一套 serif + sans serif + mono 组合
\starttypescript[serif][myPalatino]
\definefontsynonym[palarm][name:PalatinoLTStd-Roman]
\definefontsynonym[palait][name:PalatinoLTStd-Italic]
\definefontsynonym[palabf][name:PalatinoLTStd-Bold]
\definefontsynonym[palabi][name:PalatinoLTStd-BoldItalic]
\definefontsynonym[Serif] [palarm][features=fancy]
\definefontsynonym[SerifItalic] [palait][features=fancy]
\definefontsynonym[SerifBold] [palabf][features=fancy]
\definefontsynonym[SerifBoldItalic][palabi][features=fancy]
\definefontsynonym[SerifCaps] [Serif] [features=smallcaps]
\stoptypescript
\starttypescript[sans][myOptima]
\definefontsynonym[prosans][name:OptimaLTStd]
\definefontsynonym[prosansit][name:OptimaLTStd-Italic]
\definefontsynonym[prosansbf][name:OptimaLTStd-Bold]
\definefontsynonym[prosansbi][name:OptimaLTStd-BoldItalic]
\definefontsynonym[Sans][prosans][features=fancy]
\definefontsynonym[SansItalic][prosansit][features=fancy]
\definefontsynonym[SansBold][prosansbf][features=fancy]
\definefontsynonym[SansBoldItalic][prosansbi][features=fancy]
\definefontsynonym[SansCaps][Sans][features=smallcaps]
\stoptypescript
\starttypescript[mono][myMonaco]
\definefontsynonym[promono][name:Monaco]
\definefontsynonym[Mono][promono][features=default]
\stoptypescript
\starttypescript[PalatinoOptima]
\definetypeface[PalatinoOptima][rm][serif][myPalatino][default]
\definetypeface[PalatinoOptima][ss][sans][myOptima][default]
\definetypeface[PalatinoOptima][tt][mono][myMonaco][default][rscale=0.8]
\stoptypescript
在写文档时只需简单地
\usetypescript[PalatinoOptima]
\setupbodyfont[PalatinoOptima, 12pt]
这三种字体就全部定义好了。
一些奇淫技巧#
- 在同一行上放置左对齐和居中的文字
\hbox to \textwidth{\rlap{Left aligned} \hss Center aligned \hss}
或者
\startoverlay
{\leftaligned{left}} {\midaligned{middle}} % {\rightaligned{right}}
\stopoverlay