ConTeXt

在 TeX Live 2024 上执行 which context,再用 ls -l 看它给出的路径,得到的是 context -> luametatex。调用 ConTeXt 的那条命令,本尊竟是 ConTeXt 开发者为自己打造的引擎 luametatex。这一行把整件事概括完了——ConTeXt 不是往 LaTeX 上加的宏包,而是替代 LaTeX 使用的文档制作系统,而且连底层引擎都自备。本页顺着实际日志,讲清以 \setup... 为中心的写法、MkII / MkIV / LMTX 三代谱系、第一次运行为什么慢,以及究竟该由谁来选择它

ConTeXt 不是 LaTeX 的宏包

并不存在 \usepackage{context} 这种命令;在 ConTeXt 文档里写 \usepackage,运行会停在 Undefined control sequence。LaTeX 与 ConTeXt 是建在同一个 TeX 之上的兄弟格式,彼此的命令完全不通。文件名照样是 .tex,但处理它的既不是 pdflatex 也不是 lualatex,而是 context 命令。合作者或 CI 按 LaTeX 的习惯去跑而失败的事并不少见,所以在 README 里写上一行「本文档用 context main.tex 构建」很划算。

terminal
$ cat up.tex
\usepackage{amsmath}
\starttext
Hi
\stoptext

$ context up.tex
tex error       > tex error on line 1 in file ./up.tex: Undefined control sequence
mtx-context     | fatal error: return code: 1

写下 ConTeXt 的是荷兰的 Hans Hagen 与他的公司 PRAGMA ADE(Advanced Document Engineering)。名字来自「用 TeX 写的 text」,即 con-tex-t。它不是学术副产品,而是作为公司自家承接业务的生产工具长大的,这决定了它的性格:适合那种由自己设计版面的活儿,比如教材、手册、产品目录。年头之久,打开源文件也看得见——TeX Live 2024 里 cont-nl.mkxl 的文件头至今写着 version=1997.08.19author=Hans Hagencopyright={PRAGMA ADE & ConTeXt Development Team}

ConTeXt 与 LaTeX 的区别——\setup...\start…/\stop…

最大的区别在于体裁设定全部收拢进一个命令族。在 LaTeX 里,页边距归 geometry、颜色归 xcolor、脚注样式又归别的什么,各有各的语法;而在 ConTeXt 里,纸张是 \setuppapersize,正文字体是 \setupbodyfont,段落间距是 \setupwhitespace——一律以 \setup 开头,一律接 key=value。数量多吗?确实多:扫一遍 TeX Live 2024 的 LMTX 源码(tex/context/base/mkxl),以 \setup 开头的命令有 498 种。这套设计要你记的不是命令名,而是命名规则

标记这一侧同样规整。LaTeX 用 \begin{env}…\end{env} 写环境,ConTeXt 则用成对的 \start…\stop…:正文是 \starttext … \stoptext,章节是 \startsection … \stopsection,行间公式是 \startformula … \stopformula。而且没有对应 \documentclass 的强制声明。文件开头直接写 \setuppapersize[A4] 就能进正文。「先挑文档类、再找配套宏包」这套 LaTeX 式的思考步骤,就这么消失了。

latex
% first-context.tex -- compile with: context first-context.tex
\setuppapersize[A4]
\setupbodyfont[12pt]

\starttext
\startsection[title=Introduction]
  Hello, \ConTeXt!
  \startformula
    E = mc^2
  \stopformula
\stopsection
\stoptext
LaTeXConTeXt想做的事
\documentclass{article}(不需要)无需文档类声明;体裁交给 \setup...
\begin{document}\starttext正文开始;以 \stoptext 结束
\section{...}\startsection[title=...]章节;只要标题时也可以用 \section
\[ ... \]\startformula … \stopformula行间公式
\usepackage{geometry}\setuppapersize 及其余 \setup...纸张、页边距、版面;没有「加个宏包」这回事
pdflatexcontext处理命令;哪怕文件是 .tex,LaTeX 的命令也不管用

LMTX 是什么——MkII、MkIV、LMTX 三代

LMTX 是当前世代。 ConTeXt 按底层引擎的代际分成三条线:MkII 最老,跑在 pdfTeX(以及 XeTeX)之上;MkIV 是 Lua 时代的重写,跑在 LuaTeX 之上;LMTX(内部代号 MkXL)跑在 LuaMetaTeX 之上,是现行的一条。在 TeX Live 2024 上执行 context --version,会以同一日期同时报出 context.mkivcontext.mkxl——MkIV 确实仍在发行。但 context 命令本身是指向 luametatex 的符号链接,所以不加任何指定地运行,跑的就是 LMTX。运行日志第一行直说:ConTeXt ver: 2024.02.27 09:18 LMTX

terminal
$ ls -l $(which context)
context -> luametatex

$ luametatex --version
This is LuaMetaTeX, Version 2.11.02
Functionality : level 20240226

$ context --version
mtx-context     | ConTeXt Process Management 1.06
mtx-context     | main context file: .../tex/context/base/mkiv/context.mkiv
mtx-context     | current version: 2024.02.27 09:18
mtx-context     | main context file: .../tex/context/base/mkxl/context.mkxl
mtx-context     | current version: 2024.02.27 09:18

关键在 LuaMetaTeX。这是 Hans Hagen 与 ConTeXt 开发者作为 LuaTeX 的后继、专为 ConTeXt 重造的精简引擎,其大胆之处不在加了什么,而在减了什么:PDF 输出后端、字体处理和大量原语被有意从 LuaTeX 中拿掉,改由 ConTeXt 一侧的 Lua 代码实现。结果是不依赖外部库、不需要复杂的构建环境、跨操作系统移植容易。LMTX 的运行日志确实会打印 used backend: pdf,但那个 PDF 是 Lua 侧的实现写出来的。这里有一点该老实说——目前真正认真用 LuaMetaTeX 的,事实上只有 ConTeXt。把它看作 ConTeXt 的一部分,比看作通用引擎更贴近实情。

旧世代残留在哪里,看配置文件也看得出来。TeX Live 的 fmtutil.cnf 里有两行 cont-en pdftex - -8bit *cont-en.mkiicont-en xetex - -8bit *cont-en.mkii,这正是 MkII 的格式定义。也就是说 MkII 至今仍可在 pdfTeX 和 XeTeX 上烧制。而 LMTX 根本不走这套机制,如后文所述,它把格式建在自己的缓存里。新开工的话就用 LMTX,不必犹豫;MkII 与 MkIV 只当作「因为旧文档还在,所以它们还在」。

为什么只有第一次慢——mtxrun --generate 与缓存

ConTeXt 会自己把格式烧进缓存,所以只有第一次费时间。 实测:从干净状态起,第一次报 total runtime: 3.559 seconds,同一文件第二次是 0.248 seconds——差了十四倍。第一次期间在缓存里生成的,是约 19 MB 的 cont-en.fmt,之后只需读取。再往前一步,还有由 mtxrun --generate 建立的文件数据库。在没做过这一步的环境里执行 context --version,会冒出一条乍看莫名其妙的错误。

terminal
# before the file database exists
$ context --version
mtxrun          | unknown script 'mtx-context.lua' or 'mtx-mtx-context.lua'

$ mtxrun --generate            # builds the file database
mtxrun          | elapsed lua time: 3.034 seconds

$ context first.tex            # run 1: also dumps ~19 MB cont-en.fmt
system          | total runtime: 3.559 seconds

$ context first.tex            # run 2: format is cached
system          | total runtime: 0.248 seconds

这条错误的真相不是 mtx-context.lua 不存在,而是文件清单还没建好,所以找不到它。跑一次 mtxrun --generate 就解决。过了这关,ConTeXt 还有一个会让 LaTeX 用户吃惊的脾气——它会为交叉引用和目录自行多跑几遍。对带目录的文档只敲一次 context toc.tex,日志里就记着 --c:currentrun=1--c:currentrun=2 两次内部执行,还能看到上限 --c:maxnofruns=9。在 LaTeX 里要惦记「跑两遍」「上 latexmk」的事,这里是默认行为。生成的辅助文件后缀也不是 .aux 而是 .tuc

MetaPost 在同一次编译里跑完

ConTeXt 的招牌特性之一,是绘图语言 MetaPost 被内建进本体,不启动外部程序就能就地作图。在文档里写下 \startMPcode … \stopMPcode,图形便在编译过程中生成。看日志会并排出现 This is MPLIB for LuaMetaTeX, version 3.14, running in double modeloading metafun for lmtx——这正是 MetaPost 以库 MPlib 的形态住在引擎里的证据(MPlib 是 Taco Hoekwater 与 Hans Hagen 于 2007 年把 MetaPost 库化的产物)。由于 MetaPost 承袭了高德纳 METAFONT 的描述方式,线条与曲线可以按「约束」来写,而不必写死坐标。

latex
\starttext
\startMPcode
  fill fullcircle scaled 3cm withcolor .6red;
  draw fullcircle scaled 3cm withpen pencircle scaled 1mm;
\stopMPcode
\stoptext
% one run of `context` produces the PDF; no external MetaPost binary is called

这个差别比看上去大。图形不在另一个进程里,而在同一次运行之中,于是它能读到正文的尺寸:与版心等宽的图、随节号联动的装饰、由数据生成的线条与底纹——都不用后处理这一步。ConTeXt 用户连线条和页面底纹都用 MetaPost 做,原因正在于此。这和 LaTeX 里用 TikZ 的感觉略有不同:在这里,MetaPost 是直接绘制页面元素本身的工具

究竟该由谁选择 ConTeXt——说实话

答案是:由自己掌握版面设计权的人。 教材、手册、产品目录、内部出版物,以及从 XML 或数据库批量生成的排版——凡是版式由你说了算的活儿,ConTeXt 把设定收拢进 \setup... 的一体化设计就见效。反过来,投稿方指定了 LaTeX 模板的论文、学位论文就不适合。会议模板、revtex 这类文档类、合著者的环境、arXiv 的处理流程——统统以 LaTeX 为前提,迁到 ConTeXt 的功夫换不回相应的回报。这条界线无关系统高下,而在于版面的决定权在谁手里

  • 合适的活儿: 小册子、教材、产品目录、手册——凡是版面由你设计的。设定都汇进 \setup...,日后改版面只需看一个地方。
  • 合适的活儿: 从 XML 或数据自动生成的排版。ConTeXt 能直接读取并排版 XML,处理逻辑可以用 Lua 写。
  • 合适的活儿: 希望图与正文在同一份源码里的文档。MetaPost 就在同一次运行中,既无需后处理,也不必管理图片文件。
  • 合适的活儿: 幻灯片。TeX Live 2024 附带二十多种演示用样式——s-present-borings-present-darks-present-punk 等——装饰部分还可以用 MetaPost 做。
  • 不合适的活儿: 投稿方指定了 LaTeX 文档类的论文与学位论文。照模板走更快也更稳。
  • 不合适的活儿: 合著者只用 LaTeX 的稿件。关于 ConTeXt 的资料远少于 LaTeX,卡住时能问的人也有限。

资料的聚集方式也和 LaTeX 不同。LaTeX 的知识散落在世界各地的书籍、问答站与宏包手册里,而 ConTeXt 相对集中在官方 wiki contextgarden 以及《ConTeXt: an excursion》这类入门书上,还有让开发者与用户面对面的年度 ConTeXt Meeting。量少但中心清楚,这么想大致不差。再举一个很有 ConTeXt 味道的细节:这套系统提供把命令名译成母语的界面。TeX Live 2024 的 LMTX 源码里,除英语外还并列着捷克语、德语、法语、意大利语、荷兰语、波斯语、罗马尼亚语和英式英语的格式定义。荷兰语版 cont-nl 至今还在,像是一块纪念这套系统诞生之地的碑。

最后说说实际怎么起步。ConTeXt 随 TeX Live 一起发行,所以在手边这套 TeX Live 上敲 context first.tex 就够了。想一直追最新版的话,通常走 ConTeXt 官方自行发布的 LMTX 发行版(旧称 ConTeXt Standalone)——只打包与 ConTeXt 相关的部分,轻便且更新频繁。开始写时值得丢掉的一个习惯,是 LaTeX 那种「缺什么就找宏包」的反射。先用 \starttext\startsection\startitemize 搭起骨架,把页边距与字体的指定归拢到开头的 \setup...。随着章节生长再逐步添样式:设定不会散进正文,等到想改版面时,要看的地方只有一处。