TeX/LaTeX 与日语

开始用 LaTeX 写日语,多半会被告知去敲 platexuplatex。可这两个名字的可执行文件根本不存在。它们都是同一个引擎 euptex 所载入的 格式 名——敲下 platex,屏幕第一行打出来的是「This is e-upTeX」。把这一点吞下去,日语 TeX 的费解就消掉了一半。本页梳理:日语当初为什么需要专门的机制、今天还活着的三条路线(pLaTeX/upLaTeX 加 dvipdfmx、LuaLaTeX 加 LuaTeX-ja、XeLaTeX)究竟差在哪里,以及第一份文档该用哪条路开始写。

日语为什么需要自己的引擎

第一个理由很直白:最初的 TeX 每种字体只能寻址 256 个字符(最早期是 128 个)。要装下几千个常用汉字,更别说 JIS 字集里的数万字,就得动用把字体切成 256 字一块的子字体这类手段。第二个理由更深:排版规则本身就不一样。西文排版建立在「按词断行、词间空白可伸缩」这个前提上,而日语没有词的分界。既然哪儿都能断,就必须反过来把不许断的地方作为规则告诉系统。

这些「不许断的地方」就是避头尾处理:句读点、右括号、长音符、小写假名不放行首,左括号不放行尾。此外还有和文与西文相邻时插入的四分空(\xkanjiskip)、和文字符之间的间距(\kanjiskip)、标点的紧缩,以及竖排。pTeX 系引擎的解法是:给每个候选断点分配罚分——这个字落在行首扣多少分——再挑出整段罚分总和最小的那种切分方式。也就是说,把日语的规则当作罚分,灌进了 TeX 本来就有的「通盘看完整段再选最佳断行」的机制里。

platexuplatex 的区别,以及它们本是同一个引擎

区别在于各自能处理的字符范围platex 局限在 JIS X 0208 之内,uplatex 覆盖整个 Unicode。而它们并不是两个程序,而是同一个 euptex 引擎载入的两种格式。打开 TeX Live 目录下的 texmf-dist/web2c/fmtutil.cnf,这层关系就明明白白写在两行里:第一列是格式名,第二列是构建它的引擎。两行的第二列都是 euptex。「引擎与格式是两回事」这件事,在日语这边体现得比任何地方都清楚。

fmtutil.cnf
# texmf-dist/web2c/fmtutil.cnf -- format, engine, hyphenation, ini file
platex   euptex language.dat *platex.ini
uplatex  euptex language.dat *uplatex.ini

# and so the banner names the engine, not the command you typed:
$ platex  ... This is e-upTeX, Version 3.141592653-p4.1.1-u1.30-... (utf8.euc)   (preloaded format=platex)
$ uplatex ... This is e-upTeX, Version 3.141592653-p4.1.1-u1.30-... (utf8.uptex) (preloaded format=uplatex)

这行横幅值得细看。3.141592653 正是高德纳冻结的 TeX 版本号,p4.1.1 是 pTeX 扩展的版本,u1.30 是 upTeX 扩展的版本——三代履历叠在一行里。末尾的括号也有讲究:platex 报的是 (utf8.euc)uplatex 报的是 (utf8.uptex)。稿子两边都写成 UTF-8,但引擎内部保存汉字所用的编码不同。这不是抽象之谈,它会直接咬人。喂给 platex 一个 JIS X 0208 之外的字——比如「髙」「𠮷」「鷗」——它就停在这条错误上:

log
! LaTeX Error: Unicode character ^^e9^^ab^^99 (U+9AD9)
               not set up for use with LaTeX.

# same file, same class, run through uplatex instead: compiles silently.

实务上的结论很简单:新开一份文档几乎没有理由走 pLaTeX,请用 uplatex 人名里的异体字、旧字形、补充汉字、引用一句韩语或中文——没有一样是稀罕物,而每一样都能把 platex 当场卡死。仍然要选 platex 的理由只剩下两条:投稿方要求的类文件只为 pLaTeX 而写,或者你接手了既有的稿子。pTeX 系的引擎与宏,如今由 日本 TeX 开发社区 维护。

今天还活着的三条路线

实际可走的路子有三条。upLaTeX 先产出 DVI,再由 dvipdfmx 转成 PDF 的传统路线;LuaLaTeX 加 luatexja,直接输出 PDF;以及 XeLaTeX 加 xeCJK。还有第四条 pdfLaTeX 加 CJKutf8,但它要求把正文裹在 \begin{CJK}{UTF8}{min} 里,也没有像样的避头尾,所以只适合在英文论文里塞进几个日语词。三条路的分野在于:怎么拿到字体,以及通往 PDF 的路有多长。

路线类与宏包性格与适用场合
uplatex + dvipdfmxjlreqjsarticle(jsclasses)快;竖排积累最厚;期刊常指定它。到 PDF 要走两步
lualatex + luatexjajlreqltjsarticle(ltjsclasses)直接用系统的 OpenType 字体,直接出 PDF;Unicode 最强。编译较慢
xelatex + xeCJKbxjsarticle(bxjscls)等选字体省事;中日韩混排在行。细致的和文规则不及 luatexja
pdflatex + CJKutf8articleCJK 宏包只在英文里塞几个词。正文要用 \begin{CJK}{UTF8}{min} 裹住,不适合整篇

为什么只有日语这条路还要绕经 DVI

因为 pTeX 系的引擎不能直接写出 PDF。pdfTeX 把 PDF 输出内建了,而 pTeX 干到吐出 TeX 本来的输出格式 DVI 就收工。把这份 DVI 转成 PDF 的是 dvipdfmx,它负责嵌入和文字体、以及竖排所需的坐标变换。也就是说,日语的传统路线把「排版」和「生成 PDF」分给了两个人。在本机先跑 uplatex 再跑 dvipdfmx,几百字节的 .dvi 会变成几 KB 的 PDF,用 pdffonts 一看,就能见到 HaranoAjiMincho 以 CID Type 0C 的形式被嵌了进去

shell
$ uplatex doc.tex      # typeset -> doc.dvi
$ dvipdfmx doc.dvi     # convert  -> doc.pdf
$ pdffonts doc.pdf
# HaranoAjiMincho-Regular   CID Type 0C   Identity-H   emb yes

# dvipdfmx is a symlink to xdvipdfmx: one binary serves both
# the Japanese .dvi route and XeTeX's .xdv output.

顺带一提,磁盘上的 dvipdfmx 其实是指向 xdvipdfmx 的符号链接。一个二进制同时照看日语的 .dvi 和 XeTeX 产出的 .xdv。一个为日语打磨出来的转换器,最后成了全世界 XeTeX 的输出环节——这是日本造的工具汇入 TeX 主流的一例。被嵌进去的原之味字体(Harano Aji)也一样,TeX Live 里就带着,所以第一份日语 PDF 用不着另买字体。

第一份文档该用哪条路来写

写上 \documentclass{jlreq},然后用 lualatex 跑。 理由不只是质量。阿部纪行做的 jlreq 是照着 W3C 的《日语排版处理需求(JLReq)》设计的类,但实务上最叫人感激的一点是:同一份源码在 LuaLaTeX、upLaTeX、pLaTeX 上都能原样跑起来。引擎会自动判定,需要时也可以把 platexuplatexlualatex 写成类选项。这意味着最初的选择不是一锤定音的决定。实测把同一个 .tex 分别喂给 uplatexlualatex,两边都能输出嵌了原之味明朝的 PDF。

latex
% runs unchanged under lualatex, uplatex and platex
\documentclass{jlreq}
\begin{document}
こんにちは、\LaTeX。日本語の組版です。
\end{document}

想换字体时,LuaLaTeX 下载入 luatexja-fontspec,用 \setmainjfont 指定明朝、\setsansjfont 指定黑体。若要靠向传统一路,标配是给 pLaTeX/upLaTeX 用的 jsclassesjsarticlejsbook),以及 LuaLaTeX 专用的 ltjsclassesltjsarticle 等)。不过一旦选了 ltjsarticle,编译器也要跟着换成 LuaLaTeX——类与引擎对不上,是日语环境里最常见的事故。

最初会卡住的地方

日语出不来或变成乱码时,原因几乎只有两种:稿件的字符编码,或者类与引擎不匹配。请把稿子存成 UTF-8。从前 EUC-JP、Shift_JIS、ISO-2022-JP 并存,nkf 这类转换工具是必备家伙;现在不需要了。upTeX 与 LuaTeX 原生支持 Unicode,现行的 pLaTeX 也以 UTF-8 输入为前提。只有接手旧稿时才需要转换。另一个陷阱是换行符:LF 与 CRLF 混在同一个文件里,可能造成难以追查的崩坏。

  • 不要用 pdflatex 排日语正文。看到报错或乱码,先怀疑编译器设置;就算用 CJKutf8,正文也得裹在 \begin{CJK}{UTF8}{min} 里。
  • 载入 ltjsarticleluatexja 就用 lualatex 编译;用 jsarticle 就用 uplatex。是类决定引擎,所以编辑器的构建设置也要跟着改。
  • platex 报出 ! LaTeX Error: Unicode character ... not set up for use with LaTeX.,说明那个字在 JIS X 0208 之外,换成 uplatex 多半就过了。
  • 合作写稿时,把引擎名、字符编码、类名这三样写进 README 的一行。三者不齐,对方那边就复现不出来。
  • 若投稿方发的是为 platex 写的类文件,照办便是——但交稿前要确认正文里没混进异体字。