luatex 引擎 + plain TeX 格式
让 luatex plain 输出 pdf

luatex plain 从零构建

Garfileo posted @ 2010年9月23日 20:56 in LuaTeX 道阻且长 with tags luatex plain tex , 6101 阅读

本文介绍了如何从一个 tex 引擎开始,逐步构建一个什么也做不了的 TeX “发行版”。这个 TeX 发行版,本身没有什么实际的意义,但是通过动手构建它,可以从一个侧面了解一个 TeX 系统的主要构成。

准备

从 luatex 的 svn 仓库中迁出最新的源码(请参考  http://foundry.supelec.fr/gf/project/luatex/scmsvn/?action=AccessInfo):

	$ svn checkout http://foundry.supelec.fr/svn/luatex/trunk luatex-src

按照 INSTALL 文件中的说明进行编译。

若编译成功,则 luatex 程序位于 luatex-src/build/texk/web2c 目录。

可执行文件的位置

建立 luatex-plain 目录,将其作为 luatex plain 包的顶层目录,然后在该目录中创建 bin 目录及其子目录,具体如下:

$ mkdir -p luatex-plain/bin/linux

bin 目录用于可执行文件。为了便于维护,在 bin 目录中设置 linux、linux-64、mswin 之类的子目录,用于存放各个 OS 系统的可执行文件。本文只专注于 linux(x86 32)系统的可执行文件。

将编译所得 luatex 复制到 luatex-plain/bin/linux 目录。

下文如无特殊说明,所有的 TeX 程序均是在终端环境中进入 luatex-plain/bin/linux 目录后执行的。

生成 Plain TeX 格式

从 http://mirror.ctan.org/macros/plain/base/plain.tex 下载 plain.tex 文件,然后建立 luatex-plain/texmf/tex/plain/base 目录,并将 plain.tex 存于该目录。

在 luatex-plain/bin/linux 目录中执行以下命令构建 Plain TeX 格式:

$ luatex --ini plain.tex

事 实上,上述命令会挂住,提示找不到 plain.tex 文件。此时,只需要输入 plain.tex 文件的绝对路径,那么 luatex 便可以找到 plain.tex。但是,今后还会出现很多 luatex-plain 的“系统文件”需要 luatex 去寻找,例如所生成的 Plain TeX 格式文件。为了实现系统文件的自动定位,这需要 kpathsea 的帮助。

kpathsea 库

kpathsea 是一个程序库,被 TeX Live 系统用于文件查找。大凡可被 TeX Live 收录的 TeX 程序,均静态或动态连接了 kpathsea 库,luatex 自然也不例外。

kpathsea 库在查找文件时,会首先读取它的配置文件 texmf.cnf,而且它会从自己所在的目录为出发点,采用相对路径的方式找到 texmf.cnf。默认情况下,kpathsea 库会首先在它所在的目录中寻找 texmf.cnf 文件;若未能找到,它便搜索上一级目录;若依然未找到,则继续向上追溯一级目录搜索。

kpathsea 库在其当前目录及其上级目录搜索 texmf.cnf 文件时,它还会关注各层目录中的 texmf/web2c、texmf-local/web2c、share/texmf/web2c 以及 share/texmf-local/web2c 等子目录。

现在,我们建立 luatex-plain/texmf/web2c 目录,并在该目录内创建一份 texmf.cnf 文件,由于它恰好位于 luatex 程序(kpathsea 库)的上两级目录内的 texmf/web2c 目录中,因此可以肯定它会被 luatex 程序中的 kpathsea 库找到。

texmf.cnf 文件

那么,texmf.cnf 文件的内容应当是什么?答案是 kpathsea 可以解读的一组变量。例如:

TEXMFMAIN            = $SELFAUTOPARENT/texmf
TEXMF                = !!$TEXMFMAIN
TEXMFDBS             = $TEXMF
TEXINPUTS            = .;$TEXMF/tex/plain/base//

上面这份 texmf.cnf 文件,对于产生 Plain TeX 格式文件而言,足够用了。路径中的 `//` 表示路径最后的目录的子目录的递归。

在 texmf.cnf 文件中,像 TEXMFMAIN、TEXMFDBS、TEXMF、TEXINPUTS 等变量都是 kpathsea 可以识别的,它们的值构成了 TeX 目录结构(TeX Directory Structure, TDS)。

texmf.cnf 文件中的变量定义语法大致如下:

  • 如果一个变量的值是多条路径,那么各目录之间使用“;”进行间隔;
  • 如果一个变量的值是多条路径,并且路径的组成部分有些重复,可以使用简写的形式,例如 tex/plain/base 与 tex/luatex/base,可以简写为 tex/{plain,luatex}/base;
  • 如果变量的某个值是以“!!”为前缀的,例如上面 texmf.cnf 文件中的 !!$TEXMFMAIN,它表示对应的目录是被文件名数据库所管理的。

文件名数据库

kpathsea 库会根据 texmf.cnf 文件内容,去相应的目录中查找特定的文件。为了加快查询速度,kpathsea 库提供了一份用于构建文件名数据库的脚本 mktexlsr,该脚本可从 luatex-src/source/texk/kpathsea 目录中得到,将其复制到 luatex-plain/bin/linux 目录。

mktexlsr 脚本需要 kpathsea 库中的三个程序 kpseaccess、kpsestat 和 kpsewhich 的支持。这三个程序可在编译 luatex 阶段自动生成,位于 luatex-src/build/texk/kpathsea 目录,将它们复制到 luatex-plain/bin/linux 目录。

当 mktexlsr 及相关工具准备就绪后,便可用于生成文件名数据库:

$ mktexlsr

mktexlsr 脚本(实际上是 kpathsea 提供的那三个程序)会首先找到 texmf.cnf 文件,并对 texmf.cnf 文件中的 TEXMFDBS 变量所定义的目录进行扫描,将该类目录中的文件与子目录的名字记载于 ls-R 文件,并将该文件存储与 TEXMFDBS 所定义的目录内。ls-R 即文件名数据库,虽然它仅仅是一个文本文件。

texmf.cnf 文件中,对于那些拥有“!!”前缀的变量值所对应的目录,kpathsea 库会从它们所包含的 ls-R 文件中进行文件查找。对于不具有“!!”前缀值所对应的目录,则需要在文件系统(磁盘)中进行文件查找。

文件查找试验

有了 texmf.cnf 文件,有了文件名数据库,现在便可以做一个文件查找的试验了。对于 kpathsea 库提供的三个程序,其中的 kpsewhich 程序可供我们手动查找文件时使用。例如,查找 plain.tex 的路径:

$ kpsewhich plain

如果前面的各项步骤均无差错,那么当 kpsewhich 在 TeX 目录结构中查找到 plain.tex 文件时,会给出 plain.tex 的绝对路径,例如:

/opt/luatex-plain/texmf/tex/plain/base/plain.tex

如果 kpsewhich 没有在 TeX 目录结构中找到相应的文件,那么它则沉默不语。

现在略微分析一下 kpsewhich 是怎样找到 plain.tex 文件的。

首 先,kpsewhich 会明白它的任务是查找扩展名为 .tex 的文件。为了完成这个任务,它会首先找到 texmf.cnf 文件,查看其中是否定义了与 .tex 文件相关联的变量 TEXINPUTS(这种关联性是 kpathsea 内部定义的)。如果 texmf.cnf 存在 TEXINPUTS 变量 ,那么 kpsewhich 便会扫描 TEXINPUTS 变量所定义的目录进行文件查找,有可能是直接扫描文件名数据库,也有可能是扫描文件系统。对于查找到的文件,kpsewhich 便会显示它的绝对路径。

注意:texmf.cnf 文件的最后一行必须是空行!这应当与 kpathsea 库读取文件的方式有关。

产生 Plain TeX 格式

当 kpathsea 库可以正确的找到 plain.tex 文件的位置时,便可执行下面的命令生成适合 luatex 的 Plain TeX 格式。

$ luatex --ini plain

此时,luatex 会通过 kpathsea 库找到 plain.tex 的位置,并对其进行编译,以产生 Plain TeX 格式文件。但是很不幸,运行上述命令,会得到以下错误信息:

This is LuaTeX, Version beta-0.63.0-2010092018 (rev 3896)  (INITEX)
(/opt/luatex-plain/texmf/tex/plain/base/plain.tex
Preloading the plain format: codes, registers, parameters, fonts,
! Font \tenrm=cmr10 not loadable: metric data not found or bad.
<to be read again> 
                   \font 
l.401 \font
           \preloaded=cmr9
? 

这是是因为 plain.tex 预定义了一组字体,而这些字体在 luatex-plain 中尚不存在。为了解决这个问题,我们不得不再开启一个新的话题,那就是 TeX 字体安装以及如何使用 kpathsea 库查找它们。

字体

由于历史的原因,TeX 字体搞的一直都挺复杂,像怪物一般。如果对 TeX 字体知识了解很少,可以阅读这里的 tex-font.pdf 文档。如果读完后,感觉还是不很明白,那也没什么。对于这篇文章而言,只需要知道 TeX 真正需要的不是字体,只是 tfm 文件而已,并且 plain.tex 中所预定义的字体基于 CM 字体的 tfm 文件。

那么,从哪里可以得到 CM 字体的 tmf 文件?可从 http://tug.ctan.org/tex-archive/fonts/cm/tfm/ 下载 tfm.zip 文件。

建 立 luatex-plain/texmf/fonts/tfm/public/cm 目录,将 tfm.zip 解包后所得的全部 cm*.tfm 文件复制到该目录。这样,构建 Plain TeX 格式所需的字体便准备好了。但是,在 luatex 在构建 Plain TeX 格式过程中,kpathsea 库怎样可以找到所需的 tfm 文件呢?只需在 texmf.cnf 文件中添加 TFMFONTS 变量的定义即可,例如:

TEXMFMAIN            = $SELFAUTOPARENT/texmf
TEXMF                = !!$TEXMFMAIN
TEXMFDBS             = $TEXMF
TEXINPUTS            = .;$TEXMF/tex/plain/base//
TFMFONTS      	     = .;$TEXMF/fonts/tfm//

然后使用 `mktexlsr` 刷新 TeX 目录结构。

好了,现在我们继续执行 `luatex --ini plain.tex` 命令,字体加载没有问题了,但是又出现了一个新的错误:

This is LuaTeX, Version beta-0.63.0-2010092018 (rev 3896)  (INITEX)
(/opt/luatex-plain/texmf/tex/plain/base/plain.tex
Preloading the plain format: codes, registers, parameters, fonts, more fonts,
macros, math definitions, output routines, hyphenation
! I can't find file `hyphen'.
l.1222 \input hyphen
                    
Please type another input file name: 

这个错误是因为 luatex 在构建 Plain TeX 格式过程中还需要一个 hyphen.tex 文件的支持。那么,hyphen.tex 是做什么用的呢?

断字(Hyphenation)

hyphen.tex 是 TeX 用于断字的。至于 TeX 的断字知识,可参考:http://blog.bs2.to/post/EdwardLee/2172

hyphen.tex 可从 http://tug.ctan.org/macros/plain/base/ 下载,然后构建 luatex-plain/texmf/tex/generic/hyphen 目录,并将 hyphen.tex 置于其中,并修改 texmf.cnf 文件,如下:

TEXMFMAIN            = $SELFAUTOPARENT/texmf
TEXMF                = !!$TEXMFMAIN
TEXMFDBS             = $TEXMF
TEXINPUTS            = .;$TEXMF/tex/{plain,generic}/{base,hyphen}//
TFMFONTS      	     = .;$TEXMF/fonts/tfm//

然后 `mktexlsr` 刷新文件目录。

现在,再重新执行 `luatex --ini plain` 命令,终于不再报错了!当然,事实上并没有生成我们一直想得到的 Plain TeX 格式文件。

因为 Plain TeX 格式是这样生成的

首先,执行 `luatex --ini` 命令,进入 luatex 的交互环境:

$ luatex --ini
This is LuaTeX, Version beta-0.63.0-2010092018 (rev 3896)  (INITEX)
**

在 luatex 的交互环境中,“*”是 TeX 提示符,我们可以在它之后输入 TeX 控制序列,luatex 可以即时处理所输入的控制序列。为了产生 Plain TeX 格式,我们需要使用 \input 控制序列载入 plain.tex 文件,如下:

**\input plain
(/opt/luatex-plain/texmf/tex/plain/base/plain.tex
Preloading the plain format: codes, registers, parameters, fonts, more fonts,
macros, math definitions, output routines, hyphenation
(/opt/luatex-plain/texmf/tex/generic/hyphen/hyphen.tex))
*

在拥有所需的 tfm 文件以及 hyphen.tex 文件的前提下,\input plain 应当是不会出错的。

下一步,需要在 TeX 提示符后输入“\dump”控制序列,从而产生 Plain TeX 格式文件——plain.fmt。

如果,在前面执行 `luatex --ini` 命令之时,再加上一个选项“--jobname=luatex”,那么所产生的 Plain TeX 格式文件即为 luatex.fmt。

当然,也可以将所生成的 plain.fmt 直接改名为 luatex.fmt。总之,我们需要的是 luatex.fmt。这样当使用 luatex 编译 tex 文档时,它会利用 kpathsea 库寻找 luatex.fmt 文件并加载。

现在,luatex 程序与 luatex.fmt 均位于 luatex-plain/bin/linux 目录,我们需要将 luatex.fmt 文件也像 plain.tex 文件那样,将其安置于 kpathsea 库可以自动找到的目录。

luatex.fmt 的存放位置

可以模仿 TeX Live,将 luatex.fmt 存放于 $TEXMFSYSVAR/web2c/luatex 目录中。

首先,建立 luatex-plain/texmf-var/web2c/luatex 目录,并将 luatex.fmt 置于其中。

然后在 texmf.cnf 文件中添加 TEXMFSYSVAR 与 TEXFORMATS 的定义:

TEXMFMAIN            = $SELFAUTOPARENT/texmf
TEXMFSYSVAR          = $SELFAUTOPARENT/texmf-var
TEXMF                = {!!$TEXMFMAIN,!!$TEXMFSYSVAR}
TEXMFDBS             = $TEXMF
TEXFORMATS           = .;$TEXMF/web2c/luatex
TEXINPUTS            = .;$TEXMF/tex/{plain,generic}/{base,hyphen}//
TFMFONTS      	     = .;$TEXMF/fonts/tfm//

TEXFORMATS 变量的作用是告诉 kpathsea 库应当去哪里查找 fmt 文件。

最后 `mktexlsr` 刷新 TeX 目录结构。

这样,luatex 在编译 tex 文件时,便可以驾驭 kpathsea 库轻而易举的找到 luatex.fmt 文件并加载。

发布

经 过一番稍微有点曲折的折腾,现在终于完成了一个非常原始的 luatex-plain 包的构建,现在已经到了发布它的时刻。为了便于使用,并且不与系统所安装的其它 TeX 发行版发生冲突,可以模仿 ConTeXt Minimals 的方式,提供一个 setuptex 脚本供用户启用 luatex plain 环境。

在 luatex-plain 目录构建 setuptex 脚本,内容(仅限于 bash 用户)如下:

OWNPATH=$(cd -P -- "$(dirname -- "$BASH_SOURCE")" && pwd -P)
TEXROOT="$OWNPATH"
echo "Setting \"$TEXROOT\" as LuaTeX-plain root."

TEXMFOS=$TEXROOT/bin/linux
PATH=$TEXMFOS/bin:$PATH

现在可以对 luatex-plain 目录打包发布了。

本文所构建的 luatex-plain.tar.gz 可从这里下载。

如何使用

将所下载的 luatex-plain.tar.gz 解包后得到 luatex-plain 目录,假设它位于 $HOME 目录。

若使用 luatex-plain 编译如下的 test.tex 文件:

Hello LuaTeX!
\bye

只需打开终端,并输入以下命令开启 luatex-plain 环境:

$ source $HOME/luatex-plain/setuptex

或

$ . $HOME/luatex-plain/setuptex

之后,在这一终端中变可以一直使用 luatex 程序编译 tex 文件了,例如对于上述的 test.tex:

$ luatex test

输出的文件格式就像 knuth TeX 那样,是 dvi 文件。

值得注意的事

本 文所构建的 luatex-plain 没有打包任何字体,并且采用的是 knuth 的 plain.tex 文件得到的 Plain TeX 格式,因此无法输出比较现代的 pdf 文件。换言之,本文所构建的 luatex-plain 什么也做不了,但它是一个基础。

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

Avatar_small
views63 说:
2010年9月23日 21:51

要注册用户才可以?

BTW:昨晚睡前挂机 wubi 安装了个 ubuntu 10.04 netbook 感觉还不错,运行 ctx 比 XP 快。

Avatar_small
Garfileo 说:
2010年9月23日 21:57

@views63: 什么“注册用户”?

Avatar_small
views63 说:
2010年9月23日 23:48

运行 :svn checkout http://foundry.supelec.fr/svn/luatex/trunk luatex-src
提示:
认证领域: <http://foundry.supelec.fr:80> Document repository
“××”的密码:

yuewang 说:
2010年9月24日 17:27

我记得我以前发过⋯⋯

http://code.google.com/p/ctex-kit/downloads/detail?name=minimal_luatex.zip&can=2&q=

Avatar_small
Garfileo 说:
2010年9月24日 17:49

@yuewang: 嗯,我也弄过一个,当时是从 minimals 里裁剪出来的。现在,我是想弄明白里面的东西是怎样拼凑出来的。通过这样的逐步添加东西,我可以逐渐知道 luatex 的格式生成,mp 图形支持以及 Hans 的那几个字体都是怎么搞出来的。

Avatar_small
亚弥 说:
2012年10月30日 23:41

那啥……谁能告诉我,如果想做一个能生成pdf且支持中文的最小包应该咋办……

Avatar_small
Garfileo 说:
2012年10月30日 23:50

@亚弥: http://garfileo.is-programmer.com/2010/9/27/luatex-fonts.21558.html

Avatar_small
亚弥 说:
2012年10月31日 23:53

@Garfileo: 谢谢!!~


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter