2023 年 04 月 30 日
上一篇:门
下一篇:新蜗牛
蜗牛是简单的,只需要爬来爬去,偶尔经转,偶尔纬转。静物是复杂的,即使它一生只爬一次。
不知不觉,我已经为频频用于表示静物的框文设立了一堆样式参数,它们以全局变量的形式出现在 MetaPost 代码里:
numeric 框文配置.框.扩充, 框文配置.框.线粗, 框文配置.框.玩笑;
color 框文配置.文字.颜色, 框文配置.框.颜色, 框文配置.背景.颜色;
框文配置.框.扩充 := 4pt;
框文配置.框.线粗 := 2pt;
框文配置.框.玩笑 := 0;
框文配置.文字.颜色 := black;
框文配置.框.颜色 := darkgray;
框文配置.背景.颜色 := lightgray;
这些繁复的参数,若采用 Lua 的表结构表示,则非常优雅:
= {
框文配置 = {扩充 = "4pt", 线粗 = "2pt", 玩笑 = "0", 颜色 = "lightgray"},
框 = {颜色 = "lightgray"},
背景 = {颜色 = "black"}
文字 }
由于我是在 ConTeXt 中使用 MetaPost 作图,前者支持在排版代码中嵌入 Lua 程序,而且 MetaPost 本身现在亦能与 Lua 程序相互沟通,故而上述设想是可以实现的。
在 ConTeXt 排版代码中,可使用 Lua 的 table.save
函数,将 Lua 表保存到 ConTeXt 源文件所在目录,例如:
\startluacode
local 框文配置 = {
框 = {扩充 = "4pt", 线粗 = "2pt",
玩笑 = "0", 透明度 = "0", 颜色 = "lightgray"},
背景 = {颜色 = "lightgray"},
文字 = {颜色 = "black"}
}
table.save("snail-conf.lua", 框文配置)\stopluacode
在 MetaPost 绘图代码中,可使用 Lua 的 table.load
函数载入 snail-conf.lua 文件:
lua("框文配置 = table.load('snail-conf.lua')");
上述代码里的 lua
是 MetaFun 宏,其参数是 MetaPost 字符串类型,值为 Lua 代码。lua
宏通过 Lua 解释器执行其参数传入的 Lua 代码。
现在,MetaPost 绘图代码里有了一个 Lua 表 框文配置
,其中存储着框文的所有样式参数。不过,访问这些参数依然需要使用 lua
宏,例如
lua("mp.print(框文配置.框.颜色)");
结果为颜色值 lightgray
。
mp.print
是 MetaPost 定义的 Lua 函数(确切地说,是 LuaTeX 的 MPLib 库定义的函数),可将 Lua 数据转换为 MetaPost 语言支持的数据类型。
通过一个小例子,也许能够更好地理解上述的一切。假设有 ConTeXt 源文件 foo.tex,其内容如下:
\startluacode
local 配置 = {
形状 = "fullcircle",
颜色 = "darkred"
}
table.save("snail-conf.lua", 配置)\stopluacode
\startuseMPgraphic{foo}
lua("配置 = table.load('snail-conf.lua')");
pickup pencircle scaled 2pt;
draw lua("mp.print(配置.形状)") scaled 2cm withcolor lua("mp.print(配置.颜色)");\stopuseMPgraphic
\startTEXpage[offset=4pt]
\useMPgraphic{foo}
\stopTEXpage
结果为
需要注意的是,lua
是 MetaPost 宏(确切地说,是 LuaTeX 的 MPLib 库定义的宏),它的参数是字符串,只是该字符串的内容是 Lua 代码,倘若其中含有 Lua 字符串类型的数据,只能使用单引号或 [[...]]
的长字符串符号表达,从而避免与 MetaPost 的字符串的引号发生冲突。
获
和 设
定义两个宏用于简化 lua
宏:
def 获 expr a = lua("mp.print(" & a & ")") enddef;
def 设 expr a = lua(a) enddef;
例如,获
可将以下语句
draw lua("mp.print(配置.形状)") scaled 2cm withcolor lua("mp.print(配置.颜色)");
简化为
draw (获 "配置.形状") scaled 2cm withcolor (获 "配置.颜色");
框文样式配置得到大幅简化后,便可考虑将一些自定义的宏归并至一份 ConTeXt 源文件,使之可重复使用。例如
% snail.tex
\startluacode
local 配置 = {
文字 = {颜色 = "black"},
框形 = "fullsquare",
框 = {余地 = "6pt", 线宽 = "2pt", 颜色 = "darkgray"},
背景 = {颜色 = "lightgray"},
路径 = {线宽 = "2pt", 颜色 = "darkgray"},
玩笑 = "0"
}
table.save("snail-conf.lua", 配置)\stopluacode
\startMPinclusions
lua("配置 = table.load('snail-conf.lua')");
def 获 expr a = lua("mp.print(" & a & ")") enddef;
def 设 expr a = lua(a) enddef;
def 框文 (suffix name) (expr a) =
picture name;
begingroup
save 框, 文; path 框; picture 文;
文 = textext(a);
框 := fullsquare xyscaled (bbwidth 文, bbheight 文) enlarged ((获 "配置.框.余地") * (1, 1));
框 := (获 "配置.框形") xyscaled (bbwidth 框, bbheight 框);
if (获 "配置.玩笑") > 0: 框 := 框 randomized (获 "配置.玩笑"); fi;
name := image (fill 框 withcolor (获 "配置.背景.颜色");
draw 框 withpen pencircle scaled (获 "配置.框.线宽") withcolor (获 "配置.框.颜色");
draw 文 withcolor (获 "配置.文字.颜色");
);
endgroup;
enddef;\stopMPinclusions
在 ConTeXt 排版代码中,MPinclusions
环境包含的语句可被所有 MetaFun 环境共享。因此,上述 snail.tex 文件可作为模块在 ConTeXt 排版代码中使用,例如:
% foo.tex
\usemodule[zhfonts]
\input snail % 或 \environment snail
\startuseMPgraphic{foo}
设 "配置.文字.颜色 = 'darkred'";
框文(天宫, "孙悟空闹过的天宫");
draw 天宫;\stopuseMPgraphic
\startTEXpage[offset=4pt]
\useMPgraphic{foo}
\stopTEXpage
上述示例演示了 设
的用法,并略微修改了 框文
宏,以简化 框文
对象的定义。上例中的
框文(天宫, "孙悟空闹过的天宫");
等价于
picture 天宫;
天宫 := 框文("孙悟空闹过的天宫");
下一阶段的工作是逐步向 snail.tex 文件增加一些常用的宏的定义,很快便会有一个新的蜗牛模块可用。
倘若并非在 ConTeXt 环境中使用 MetaPost 语言作图,上述定义模块的方式并不通用。不过,无论使用何种 TeX 宏包,只要 TeX 引擎是 LuaTeX(或 LuaMetaTeX,虽然不太可能),移植上述模块中的代码,在理论上是可行的。例如,可使用 \directlua
保存配置表,使用 MetaPost 的 input
宏代替 MPinclusions
环境。