上一篇:ConTeXt 计算机

ConTeXt 里的 Lua。这句话博大精深。

ConTeXt 世界里的大部分设施是用 TeX 语言构建的。在 ConTeXt 源文件里, \ 后面跟随一个或多个英文字母的文本称为控制序列。 \ 后面跟随单个非字母的文本,也称为控制序列。

例如

\starttext
foo \ConTeXt\ bar
\stoptext

其中,\starttext\ConTeXt\stoptext 以及 \空格,皆为控制序列。

所有的控制序列有着一个共同的使命,将排版信息输出到 PDF 文件里,简而言之,即排版。例如上例,排版结果如下图所示

每个控制序列都有其独特的作用。例如,在 ConTeXt 的世界里,只有位于 \starttext\stoptext 之间的内容,方有机会输出至 PDF 文件。

TeX 语言能够组合既有的控制序列,派生出新的控制序列,这是 TeX 世界生生不息的根源所在。不妨从人类语言字词组合的角度理解控制序列的组合,例如「人」和「间」的组合产生了「人间」。

TeX 语言所构造的世界在 TeX 源文件里占主体地位。Lua 语言通常仅出现于控制序列 \ctxlua{ ... }

\startluacode
... ... ...
\stopluacode

中省略号指示的位置,其中 \ctxlua\startluacode 以及 \stopluacode 是沟通 TeX 世界和 Lua 世界的隧道,但 Lua 世界里通过计算产生的结果最终需要传递于 TeX 世界。

因此,TeX 世界看了 Lua 世界一眼说,世界是我们的,也是你们的,但归根结底是我们的。

Lua 世界说,归根结底是 PDF 的。

现代大多数字处理软件,例如微软 Word,金山 WPS,自由的 LibreOffice Writer……它们皆能将自己的排版结果转化为 PDF 文件。现在的网页浏览器也能将网页内容保存为 PDF 文件。有什么必要使用 TeX 制作 PDF 文件呢?

同样是照片,手机拍出来的,跟单反相机拍出来的,通常也不是一回事。TeX 很像单反相机。

倘若对排版有所追求,TeX 就值得学习。

倘若 TeX 值得学习,ConTeXt 也就值得学习。

倘若 ConTeXt 值得学习,ConTeXt 里的 Lua 也值得学习。因为 Lua 语言能让 TeX 世界更容易生生不息。

TeX 语言让 TeX 世界生生不息的方式像是一群原始人努力维护着一个火堆,以免断了火种。当 Lua 语言出现在 TeX 世界里时,这个火堆就变成了火柴,打火机,火焰喷射器……

为了充分说明这一点,先了解一下 \title 这个控制序列,它用于排版文章的标题。例如

\starttext
\title{Hello world!}

... ... some text ... ...

\stoptext

假设上述内容是 ConTeXt 源文件 foo.tex 的全部内容,执行

$ context foo.tex

命令里,可无须写 .tex,以下命令与上述命令等价:

$ context foo

结果得到的 foo.pdf,它在我机器上的 PDF 阅读器里的样子如下图所示:

显然,作为标题的 Hello world! 的字号要比正文 ... ... some text ... ... 大了许多,这是很合理的,因为标题需要醒目。

ConTeXt 默认的标题样式并不是太好看,但是它允许用户自定义标题样式。例如,

\setuphead[title][align=middle]

\starttext
\title{Hello world}

... ... some text ... ...

\stoptext

在重新生成的 PDF 文件里,标题就位于页面正中了。

也可以设置标题的颜色,例如将其设置为暗绿色:

\setuphead[title][align=middle,color=darkgreen]

效果如下图所示:

以上对文章标题的位置和外观的调整,展现了 TeX 世界的能力所及之万一,尽管如此,倘若我提出一个并不多么刁钻的问题,如何让标题里的字母序列从绿色渐变为红色?就会让 TeX 世界感到乏力。若有不信,可尝试使用 TeX 语言解决以下两个问题:

  • 遍历一段文本,访问它的每个字符。
  • 小数的加法运算,例如计算 0.1 + 0.3。

这两个问题,实质上正是解决我上面提出的让标题字符颜色渐变问题的关键。第一个问题,阅读《The TeX book》到第 20 章的时候,倘若依然气定神闲,兴许举手之劳便可解决。至于第二个问题,倘若不动用一些 TeX 黑客们煞费苦心编写的宏包 1,那就要自己煞费苦心去写这样的宏包——需要大量的控制序列的组合,方能解决。

Lua 世界如何解决文本的遍历问题呢?在 Lua 语言里,一段文本可以表示为一个字符串,例如

local title = "Hello world!"

要遍历 title 的每个字符并赋之以渐变的颜色,只需

local red, green, step  = 0, 1, (1 / #title)

function fancy_char(c)
    red = red + step
    if red > 1 then red = 1 end
    green = green - step
    if green < 0 then green = 0 end
    context("\\definecolor[fancy][r=%f,g=%f,b=0]", red, green)
    context("\\color[fancy]{" .. c .. "}")
end

string.gsub(title, ".", fancy_char)

上述 Lua 代码可产生经 Lua 解释器执行后,可产生一组 TeX 控制序列,它们与以下控制序列等价:

\definecolor[fancy][r=0.083333333333333,g=0.91666666666667,b=0]%
\color[fancy]{H}%
\definecolor[fancy][r=0.16666666666667,g=0.83333333333333,b=0]%
\color[fancy]{e}%
\definecolor[fancy][r=0.25,g=0.75,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.33333333333333,g=0.66666666666667,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.41666666666667,g=0.58333333333333,b=0]%
\color[fancy]{o}%
\definecolor[fancy][r=0.5,g=0.5,b=0]%
\color[fancy]{ }%
\definecolor[fancy][r=0.58333333333333,g=0.41666666666667,b=0]%
\color[fancy]{w}%
\definecolor[fancy][r=0.66666666666667,g=0.33333333333333,b=0]%
\color[fancy]{o}%
\definecolor[fancy][r=0.75,g=0.25,b=0]%
\color[fancy]{r}%
\definecolor[fancy][r=0.83333333333333,g=0.16666666666667,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.91666666666667,g=0.083333333333333,b=0]%
\color[fancy]{d}%
\definecolor[fancy][r=1.0,g=0,b=0]%
\color[fancy]{!}%

这组 TeX 控制序列可在 PDF 文件里输出颜色渐变的文字:

倘若将上述的 Lua 代码嵌入到 ConTeXt 源文件里,便可实现文章标题颜色的渐变了。例如

\startluacode
local title, step
local red, green = 0, 1

function fancy_char(c)
    red = red + step
    if red > 1 then red = 1 end
    green = green - step
    if green < 0 then green = 0 end
    context("\\definecolor[fancy][r=%f,g=%f,b=0]", red, green)
    context("\\color[fancy]{" .. c .. "}")
end

function fancy(text)
    title = text
    step = 1 / #title
    string.gsub(title, ".", fancy_char)
end
\stopluacode

\define[1]\fancy{\ctxlua{fancy([=[#1]=])}}
\setuphead[title][align=middle,deeptextcommand=\fancy]

\starttext
\title{Hello world!}

... some text ...

\stoptext

生成的 PDF 如下图所示:

倘若此刻看不懂上述代码,勿须焦虑。就像每天出门,我们看到的这个世界,谁能真正熟悉它呢?上述代码,展现的是一个复杂的世界的概貌速写……不对,是两个世界的……而造访它们的旅程,从此刻刚刚开始。

下一篇:卡片

garfileo
6k 声望1.9k 粉丝

这里可能不会再更新了。