\chapter[misc]{杂技}

前面章节所述内容足以应对风格不甚讲究的文档排版任务，例如一些个人文档，如日记、随笔、读书笔记之类。从学习的角度而言，我也很建议先用 \CONTEXT\ 完成此类任务，从而逐渐熟悉其基本用法。但现实中总有一些非常规的要求。一些特定的问题，其解决方法难以独立构成章节，故而我将其汇总为一章，并且自由发挥，想到什么，便写什么。可将这部分内容视为 \CONTEXT\ 在微观视角下的形态。

\section{标题}

使用 \tex{title} 命令排版无编号的一级标题时，默认是另起一页，若不希望如此，可作以下设定：

\startTEX
\setuphead[title][page=no]
\stopTEX

在排版手册或书籍时，若文档分成多章，则章的序号的形式通常是「第 x 章」，若构造该形式的序号，若主语言界面\footnote{\CONTEXT\ 为一些关键词提供了多语言环境，例如英文的 Figure、Table、Chapter 等关键词，在中文界面下分别是图、表、章等。}为英文，则可以像下面这样设定章序号的前缀和后缀。

\startTEX
\setuplabeltext
  [en]
  [chapter={{第\,},{\,章}}]
\stopTEX

\noindent 若主语言界面是中文，就按中文界面设定，如下：

\startTEX
\mainlanguage[cn]
\setuplabeltext
  [cn]
  [chapter={{第\,},{\,章}}]
\stopTEX

\noindent 上述代码中的「\tex{,}」，之前讲过，它能够制造一个较小的间距，比它略宽一些的是「\tex{;}」，再宽一些便是空格字符。

如果你是用 zhfonts 模块设定字体，该模块里已经为你设置好了章序号的形式，而且这也是 zhfonts 唯一做的额外的事。

比章（chapter）更高级别的标题是部分（part），如果你想顺便设定「第 x 部分」，只需

\startTEX
\setuplabeltext
  [cn]
  [part={{第\,},{\,部分}}]
\stopTEX

上述设定的章序号形式，它无法作用于目录，亦即目录里的章序号依然是默认的单纯数字形式。要设定目录中的章节序号，需要用 \tex{setuplist} 命令。所谓目录，本质上是嵌套形式的列表结构，每个层级的列表样式皆可用该命令设定。本文档的目录样式，其中章标题所在层级列表的样式设定如下：

\startTEX
\def\ChapterNumber#1{第 #1 章\quad}
% 章标题层级的样式
\setuplist
  [chapter]
  [alternative=a,              % 章标题层级使用目录样式 a
   before={\blank[halfline]},  % 章标题之前留半行间距
   after={\blank[halfline]},   % 章标题之后留半行间距
   style=bold,                 % 设置字体为粗体
   width=fit,                  % 设定章序号的宽度为自适应
   pagenumber=no,              % 不显示页码
   numbercommand=\ChapterNumber]
\stopTEX

\noindent 上述代码的关键之处在于，通过 \type{numbercommand} 能够传入我们自定义的宏，由后者生成我们所期望的章序号。\CONTEXT\ 的很多样式设定命令支持类似的技法。\tex{setuplist} 命令各项参数的含义见文档\cite[setuplist]。

\section{特定页面}

如果你设定了页眉或页脚，默认情况下，它们除了会出现在普通页面上，也会出现在各章首页，但有些文档格式要求各章首页不需要出现页眉和页脚。要达到这个效果，需要做以下设定：

\startTEX
\setuphead[title,chapter][header=empty,footer=empty]
\stopTEX

\noindent \tex{setuphead} 这类样式设定命令，若多次使用，且每次设定不同的参数，则这些设定的结果是叠加的。

如果你的文档是双面排印，每章首页的前一页可能是空白页，这些页面上的页眉和页脚通常也应该消除。解决方法是，为各章首页定义断页策略，例如

\startTEX
% 双面排印
\setuppagenumbering [alternative=doublesided]
% 断页策略
\definepagebreak[mychapterpagebreak][yes,header,footer,right]
% 让段页策略生效
\setuphead[chapter][page=mychapterpagebreak]
\stopTEX

\noindent 上述的 \tex{definepagebreak} 命令参数，可以解读为：定义断页策略 \type{mychapterpagebreak}，第一个参数 \type{yes} 表示必须断页。后面三个参数的含义是，在右页（\type{right}）上放置页眉和页脚，亦即左页无页眉和页脚。

对于各章首页的前页是空页的断页过程，可以理解为，本来是一个页面，现在需要另起一页，前者的内容转移到后者，然后将前者清空，但是可以设定这个转移过程是否要带走页眉和页脚。由于 \CONTEXT\ 的双面排印默认是将每章断页后的内容放在右页的，故而上述代码中的 \type{right} 可省略。其他类型的标题的样式，也都支持 \type{page} 参数，故而可以用类似的方式为其设定断页策略。

\tex{setuphead} 的 \type{page} 参数，其值默认是 \type{yes}，表示每章都是另起一页，这也是为何 \tex{definepagebreak} 的第一个参数是 \type{yes} 的原因，因为后者会覆盖 \tex{setuphead} 的 \type{page} 参数的原有值，故而需要显式提供 \type{yes} 以维持每章首页的默认的分页样式。

\section{奇怪的间距}

若只是单纯插入一张图片，令其独占一段且居中，而你不想用 \tex{placefigure} 命令，可以像下面这样做，但是所得结果可能是你意想不到的，插图顶部距离上一段太近，而底部又有一些间距。

\startexample
\startTEXpage[width=6cm]
... some text ...\par
\midaligned{\externalfigure[foo.png]}
... some text ...
\stopTEXpage
\stopexample
\example[option=TEX][strange-blank]{上下间距不对称}{\externalfigure[16/foo.pdf][frame=on]}

这种奇怪的现象，在 \in[drawing-sym] 节已遇到过了，原因是 \tex{externalfigure} 载入的外部图形也是没有基线，结果导致其位置偏上。此时，插图的底线与下一行文字的基线刚好是一行文字的高度，需要让插图下沉半个行高，才能让其上下间距近似对称，见下例。

\startexample
\startTEXpage[width=6cm]
... some text ...\par
\midaligned{\externalfigure[foo.png]}
\blank[-halfline]
... some text ...
\stopTEXpage
\stopexample
\example[option=TEX][strange-blank]{上下间距对称}{\externalfigure[16/foo-2.pdf][frame=on]}

\noindent \tex{blank} 命令能在竖直方向上插入负的间距。如果用 \tex{hbox} 将插图包含起来，也可以用 \tex{lower} 命令让插图下沉。

\tex{blank} 命令是 \CONTEXT\ 特有的。构造竖向间距，更为基本的 \TEX\ 命令是 \tex{vskip}，若实现上例等效的负的竖向间距，还需要用 \tex{lineheight} 命令获得行高，即

\startTEX
\vskip-.5\lineheight
\stopTEX

构造横向间距的 \TEX\ 基本命令是 \tex{hskip}，其用法已在 \in[breaking-lines] 中涉及，它也支持负的横向间距，例如 \type{a\hskip-1em b} 的结果是\;\;a\hskip-1em b\;\;。

\section{编组}

\TEX\ 默认是以一对花括号构造一个编组（Group）。基于编组，你可以将很多字符、插图、表格等元素组织成一个对象，然后将该对象作为参数值传递给 \TEX\ 命令（或宏）。下面以一个简单的宏为例，阐述编组的重要性。

以下代码定义了一个简单的宏 \tex{foo}，它接受两个参数，然后给每个参数增加方括号，亦即这个宏的展开结果是它所接受的两个参数分别套上了方括号。

\startTEX
\def\foo#1#2{[#1][#2]}
\stopTEX

如果像下面这样使用 \tex{foo}：

\startTEX
\foo 路漫漫其修远兮
\stopTEX

\noindent 则结果为

\startTEX
[路][漫]漫其修远兮
\stopTEX

如果像下面这样使用 \tex{foo}：

\startTEX
\foo{路漫漫}{其修远兮}
\stopTEX

\noindent 则结果为

\startTEX
[路漫漫][其修远兮]
\stopTEX

通过以上示例，你大概能从直觉上认识到，\TEX\ 会将一个编组视为一个字符，颇似庄子所说的，泰山莫大于秋毫之末。

编组另一个重要的特性是它能构造局部环境，在该环境内所作的任何设定，不会影响编组之外的一切。这个特性，在你需要临时设定一些会影响全局的样式参数时颇为有用。例如，如果你临时想将文本框的颜色设为红色，可以像下面这样做：

\startexample
... {\setupframed[framecolor=red]\inframed{demo}} ...
\stopexample
\simpleexample[option=TEX]{\getexample}

花括号较为单薄，辨识度太低，而且有些情况下无法使用花括号，例如

\startTEX
\def\foo{{}
\def\bar{}}
\stopTEX

\noindent 也许你会以为 \tex{foo} 的展开结果是左花括号，而 \tex{bar} 的展开结果是右花括号，但这是不可能的。事实上，\tex{foo} 的展开结果是

\starttyping
{}
\def\bar{}    
\stoptyping

\noindent 原因宏定义里出现了未成对的花括号，而 \TEX\ 编译器会努力为其找到配对的花括号。为了解决这类问题，\TEX\ 提供了 \tex{begingroup} 和 \tex{endgroup} 作为左右花括号的替代。\CONTEXT\ 提供了更为简单的等效命令 \tex{start} 和 \tex{stop}，故而上述示例可改写为以下的正确形式：

\startTEX
\def\foo{\start}
\def\bar{\stop}
\stopTEX

\CONTEXT\ 的环境命令，例如 \type{\starttext ...\stoptext}，本质上就是由 \tex{start} 和 \tex{stop} 构成的编组。理解了这一点，就可以断定 \tex{placetable} 这样的命令，实际上无需用花括号包含 \type{tabulate} 这类环境，亦即在文档中插入一个表格，可以像下面这样写。

\startTEX
\placetable{标题}
\starttabulate[|c|c|]
\NC 1 \NC 2 \NR
\stoptabulate
\stopTEX

\noindent 在 \in[placeformula] 节，你已见识过了 \tex{placeformula} 省略花括号的写法：

\startTEX
\placeformula[formula-foo]
\startformula
\int_0^{+\infty}f(x) {\rm d}x
\stopformula
\stopTEX

\section{样式切换}

使用 \type{setups} 环境能够定义一组样式，便于在文档中根据需要予以切换。下例为盒子定义了两个不同的环境，并演示了环境的切换方法。

\startexample
% foo 样式
\startsetups foo
  \setupframed[framecolor=red, rulethickness=2pt]
\stopsetups
% bar 样式
\startsetups bar
  \setupframed[framecolor=blue, rulethickness=4pt]
\stopsetups
% 切换为 foo 样式
\setup[foo]\inframed{foo 环境}\quad
% 切换为 bar 样式
\setup[bar]\inframed{bar 环境}
\stopexample
\simpleexample[option=TEX]{\getexample}

不过，样式切换也没有太深的玄机，通过定义一些简单的宏，用这些宏封装一些样式设定语句，也能产生类似效用。

\section{非常规序号}

如果一份文档是由多章构成，插图和表格，这些浮动对象的序号默认是每章都会重置，即从 1 开始，且以该章的序号为前缀，例如「图 2.3」，表示第 2 章的第 3 幅插图。有些文档格式要求的是插图和表格的序号贯穿全文，亦即序号从 1 开始，一直递增，直至文档结束，要实现这种效果，可以用以下设定：

\startTEX
\setupcaption[figure][way=bytext,prefix=no]
\setupcaption[table][way=bytext,prefix=no]
\stopTEX

若不想为每个浮动对象的标题设定上述样式，也可以用以下命令统一设定：

\startTEX
\setupcaptions[way=bytext,prefix=no]
\stopTEX

\noindent \CONTEXT\ 默认的浮动对象序号的样式是「\type{way=bychapter, prefix=yes}」。

插图、表格以及公式的序号，有些文档格式认为以短横线作为间隔符会更好看一些，例如「\type{2-3}」。\CONTEXT\ 超人 Wolfgang Schuster 给出的解决方案是

\startTEX
\setupcaptions[prefixsegments=chapter,prefixconnector=-]
\stopTEX

\noindent 上述设定，可将图片、表格的序号转变为 x-y 形式。至于数学公式，亦可作类似设定，例如：

\startTEX
\setupformulas[prefixsegments=chapter,prefixconnector=-]
\stopTEX

以下代码在局部环境验证了上述方法是否有效。

\startTEX
\start
\setupformulas[prefixsegments=chapter,prefixconnector=-]
\placeformula
\startformula
a^2 + b^2 = c^2
\stopformula
\stop
\stopTEX
\start
\setupformulas[prefixsegments=chapter,prefixconnector=-]
\placeformula
\startformula
a^2 + b^2 = c^2
\stopformula
\stop

\setupmathematics[integral=nolimits]
\startformula
\int_{-\infty}^{x}\frac{dx}{H(x)}
\stopformula

有时，一个公式存在不同的形式，有些文档格式要求用字母后缀以示区别，例如 2.3a，2.3b 之类，\CONTEXT\ 的数学环境将这类公式称为子公式，见下例。

\start
\switchtobodyfont[9pt]
\startexample
然后，对可信性 $(BC|D)$ 应用基本函数 $F$，可得：

\startsubformulas
\placeformula[eq:1]
\startformula
(ABC|D) = F\{F[(C|D), (B|CD)], (A|BCD)\}
\stopformula

不过，也可以在一开始认为 $AB$ 是一个命题，这样就可以从另一种顺序推出不同的结果：

\placeformula[eq:2]
\startformula
(ABC|D) = F[(C|D), (AB|CD)] = F\{(C|D), F[(B|CD), (A|BCD)]\}
\stopformula
\stopsubformulas
\stopexample
\simpleexample[option=TEX]{\null}
\startframedtext[width=broad]
\getexample
\stopframedtext
\stop

\noindent 如果需要消除字母后缀之前的间隔符号，可以用以下设定：

\startTEX
\defineseparatorset[none][]
\setupformulas[numberseparatorset=none]
\stopTEX
\start
\defineseparatorset[none][]
\setupformulas[numberseparatorset=none]
\switchtobodyfont[9pt]
\startframedtext[width=broad]
\getexample
\stopframedtext
\stop

\section{三段论}

\CONTEXT\ 为数学公式提供了 \type{matrix} 环境，能够像 \type{tabulate} 表格那样构造矩阵，见下例。在数学公式里，可以用 \tex{text} 命令构造一个嵌入式的 \TEX\ 环境，有些像抄录（Verbatim）环境里的 \TEX\ 逃逸。

\startexample
\placeformula
\startformula
\startmathmatrix
  \NC \text{若 $A$ 为真，则 $B$ 为真} \NR
  \NC \text{$A$ 为真} \NR
  \HL
  \NC \text{所以，$B$ 为真} \NR
\stopmathmatrix
\stopformula
\stopexample
\simpleexample[option=TEX]{\null}
\getexample

不过，\type{matrix} 环境只是语法像 \type{tabulate}，但二者于本质上是有区别的。\type{tabulate} 内容中的每一行可以用 \type{\NC\NR} 结尾，例如

\startTEX
\starttabulate[|c|]
\NC 1 \NC\NR
\stoptabulate
\stopTEX

\noindent \type{matrix} 不可如此，否则会导致矩阵的最后一列是一个空列，例如

\startexample
\placeformula
\startformula
\startmathmatrix
  \NC \text{若 $A$ 为真，则 $B$ 为真} \NC\NR
  \NC \text{$A$ 为真} \NC\NR
  \HL
  \NC \text{所以，$B$ 为真} \NC\NR
\stopmathmatrix
\stopformula
\stopexample
\simpleexample[option=TEX]{\null}
\getexample

\noindent 上例结果中的横线，要比前例略微向右伸出些许，此即矩阵空列所致，而 \type{tabulate} 环境无此问题，大概是因为它允许最后一列为空列，以便为表格设定右侧边界线。

\section{Lua 宏}

如果你也觉得 \TEX\ 宏编程是难以掌握的怪物，且觉得能用 Lua 语言为 \TEX\ 增加一些功能要更为容易——在 \in[lua] 节里已作基本介绍，那么不妨了解一下 \CONTEXT\ 为此类需求提供的更为直观的编程接口，下例利用了该接口重新实现了一个可将数字转化为带圈形式的宏。

\startexample
\startluacode
interfaces.implement {
  name = "circled",
  public = true,
  arguments = {"integer"},
  actions = function(x) context('\\char"' .. tostring(2460+x-1)) end
}
\stopluacode
% 测试
\circled{3}\circled{6}\circled{1}
\stopexample
\simpleexample[option=TEX]{\getexample}

\noindent 上述代码中的 \type{name} 是宏名；\type{public} 必须设定为 \type{true}，表示该接口能以宏的形式调用；\type{arguments} 是参数表，\type{integer} 表示参数类型为整型数；\type{actions} 的值是 Lua 匿名函数，当接口以宏的形式调用时，便会触发该函数。

如果要定义多参数宏，只需依序在 \type{arguments} 表里给出类型，例如

\startTEX
arguments = { "string", "integer", "boolean", "dimen" }
\stopTEX

\noindent 表示接受 4 个参数，类型依序为字符串、整型数、布尔值、尺度。

参数类型也可以是 Hash 表。这类参数通常是用于定义具有选项参数的宏，亦即其参数为方括号形式，见下例。

\startexample
\startluacode
interfaces.implement {
  name = "upper",
  public = true,
  arguments = {"hash"},
  actions = function(x)
    if x.style == "bold" then
      context("{\\bf " .. string.upper(x.text) .. "}")
    else
      context(string.upper(x.text))
    end
  end
}
\stopluacode
% 测试
\framed{\upper[text=demo,style=bold]}
\stopexample
\simpleexample[option=TEX]{\getexample}

\subject{结语}

至此，本文档所介绍的绝大多数 \CONTEXT\ 排版命令，应付常规的技术文档排版任务，绰绰有余。然而，ConTeXt 还有许多更高深的命令，其用法对我而言是超纲了……不过，无论 \CONTEXT\ 提供了多少排版命令，也无法覆盖现实中全部的排版需求，前者是有限的，而后者总是会发生变化。庄子说，指穷于为薪，火传也，不知其尽也。若将排版命令视为薪柴，那么 \CONTEXT\ 之火如何传续呢？答案是，编程。
