上一篇:学一点 Lua
基于 ConTeXt 的缓冲区(Buffe)及其对 Lua 语言的支持,可以实现大段排版内容的预处理。所谓预处理,是指在 TeX 编译器对排版内容进行断行分页之前的处理,通常由排版者负责。
标点间距压缩
ConTeXt 对汉字排版仅提供了基本支持,许多排版上的细节问题,通常需要用户自行实现,例如标点间距压缩。汉字是方块字(等宽字体),标点符号自然也是方块字。当两个标点符号相邻时,倘若不压缩它们的间距,排版结果会显得过于粗疏。例如,
\environment card-env
\starttext
老子:「天下万物生于有,有生于无。」
\stoptext
冒号和左引号的间距过大,句号和右引号的间距也过大,用 \kern
插入负间距可予以调整:
\environment card-env
\starttext
老子:\kern-.5em「天下万物生于有,有生于无。\kern-.5em」
\stoptext
显然每次在写标点符号时,手工加入 \kern
调整标点间距,容易累手甚至出错。需要考虑,如何写一个预处理程序,让它代替我们完成这些工作。
缓冲区
缓冲区可用于收集内容。例如
\environment card-env
\starttext
\startbuffer[foo]
老子:「天下万物生于有,有生于无。」
\stopbuffer
\color[darkred]{\getbuffer[foo]}
\color[darkgreen]{\getbuffer[foo]}
\color[darkblue]{\getbuffer[foo]}
\stoptext
\startxxx ... \stopxxx 宏
基于缓冲区,可以构造类似 \starttext ... \stoptext
这样的宏,例如
\environment card-env
\def\startfoo{\dostartbuffer[foo][startfoo][stopfoo]}
\def\stopfoo{\getbuffer[foo]}
\starttext
\startfoo
老子:「天下万物生于有,有生于无。」
\stopfoo
\stoptext
其中,\dostartbuffer
是 ConTeXt 的系统宏(System macros),其作用是定义一个名为 foo
的缓冲区,并且定义 \startfoo
和 \stopfoo
作为缓冲区的开始和结束。由于 TeX 宏可以重定义,所以可将 \stopfoo
重定义为 \getbuffer[foo]
直接呈现缓冲区内容。
如果将 \stopfoo
重定义为 \ctxlua{...}
宏,便有机会将缓冲区 foo
的内容转移给一段 Lua 程序,因为 ConTeXt 提供了与 \getbuffer
等效的 buffers.getbuffer
函数。例如,
\environment card-env
\def\startfoo{\dostartbuffer[foo][startfoo][stopfoo]}
\def\stopfoo{\ctxlua{context(buffers.getcontent('foo'))}}
\starttext
\startfoo
老子:「天下万物生于有,有生于无。」
\stopfoo
\stoptext
其中,\stopfoo
被重新定义为嵌入 Lua 程序的宏,所嵌入的 Lua 程序
context(buffers.getcontent('foo'))
可在 Lua 世界里以 Lua 字符串的形式从缓冲区 foo
中获取内容,并将所获内容提交给 context
函数,由后者转发给 TeX 世界。
文本匹配与替换
在 Lua 世界里,处理文本的利器是 LPEG 库。对于标点间距压缩的问题,使用 LPEG 库,仅费吹灰之力,例如
local P = lpeg.P
local Cs = lpeg.Cs
local p = Cs((P':「'/':\\kern-.5em「' + 1)^0)
local q = Cs((P'。」'/'。\\kern-.5em」' + 1)^0)
print(q:match(p:match([[老子:「天下万物生于有,有生于无。」]])))
Cs
为 LPEG 库的捕获替换模式,它可以从给定的字符串里捕获与模式 P':「'
和 P'。」'
匹配的子串,并分别将其替换为 :\\kern-.5em「
和 。\\kern-.5em」
。
将上述 Lua 代码略加改动,便可嵌入到 ConTeXt 源文件:
\environment card-env
\startluacode
my = my or {}
function my.puncs_compress(buffer)
local P, Cs = lpeg.P, lpeg.Cs
local p = Cs((P':「'/':\\kern-.5em「' + 1)^0)
local q = Cs((P'。」'/'。\\kern-.5em」' + 1)^0)
context(q:match(p:match(buffer)))
end
\stopluacode
\def\startfoo{\dostartbuffer[foo][startfoo][stopfoo]}
\def\stopfoo{\ctxlua{my.puncs_compress(buffers.getcontent('foo'))}}
\starttext
\startfoo
老子:「天下万物生于有,有生于无。」
\stopfoo
\stoptext
使用 ConTeXt 实现的 Lua 库(l-lpeg.lua)里的 lpeg.replacer
函数,可将上述 my.puncs_compress
函数的定义简化为:
function my.puncs_compress(buffer)
local rep = {
[1] = {':「', ':\\kern-.5em「'},
[2] = {'。」', '。\\kern-.5em」'}
}
context(lpeg.replacer(rep):match(buffer))
end
结语
倘若熟悉 LPEG 库,在 ConTeXt 缓冲区里,会觉得自己是个强大的巫师,举手投足,便是黑魔法。
下一篇:源码凸显
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。