LaTeX 利用 TECKit 映射字符的方法

在使用 LaTeX 进行中文文档的撰写中,有一些中文的标点符号用普通输入法输入非常麻烦,比如,知乎 风格的直角引号 「 」 『 』U+300CU+300F),科技文献特别是数学文献中为了避免与下标 0o 混淆而使用的实心句号 U+FF0E),以及书名中再次使用书名的单书名号 〈 〉U+3008U+3009),党政机关公文文号中使用的六角形括号〔 〕U+3014U+3015)等等。有些符号尚可以在 LaTeX 中利用相近符号替代,比如 可以直接使用西文的 .〈 〉 则使用西文的 < > 或使用数学公式中的尖括号 \langle \rangle (这两个符号长这样:\langle \rangle)等替代,但这些替代方案的字形(glyph)及其与其他字符之间的字偶间距都是不合适,实际上是一种错误的表达方法,就好像用 x 来表示 ×\times,用“日”字来表示“曰”字 一样。当然,这些符号都有对应的 Unicode 编码,不过,使用 \symbol + Unicode 编码可不是一种合适的解决方案,毕竟,谁脑子里能记得住这么多Unicode 编码呢? xeCJK 宏包为字体设置提供了 Mapping 选项,在一定程度上可以解决这些问题。本文简单介绍一下实现方法。

xeCJK 提供的映射

xeCJK 提供了四个 TECKit 映射文件,可以在设置字体的时候通过 Mapping 选项来使用它们。分别是:

  • fullwidth-stop 将字体的空心句号 映射成为实心句点
  • full-stop 将字体的实心句点 映射为空心句号
  • han-simp 将字体中的繁体中文字符映射为简体中文字符。
  • han-trad 将字体中的简体中文字符映射为繁体中文字符。

后两个文字字符映射需要注意,这里只是字符机械一一映射,在汉字简化过程中,有很多字进行了合并,如“后”和“後”统一简化成“后”,“鐘”和“鍾”统一简化成“钟”,在使用 Mapping=han-trad 选项后不会根据上下文将“后来”改为“後来”,“闹钟”将被改成“鬧鍾”而不是正确的“鬧鐘”,不过这比 Microsoft Word 把“菠萝”改成“鳳梨”,“海内存知己”改成“海記憶體知己”要强得多了……

闲话不多说,切入正题。Mapping=fullwidth-stop 实际上就是实现科技文献中使用实心句点 的方法,只要给正文 CJK 字体加上这一字体特性选项,在书写源文档时使用正常的句号 ,编译后就会将 映射为 输出。简单一个 MWE 如下:

1
2
3
4
5
6
\documentclass{ctexart}
\usepackage{zhlipsum}
\setCJKmainfont{SimSun}[Mapping=fullwidth-stop]
\begin{document}
\zhlipsum[1][name=zhufu]
\end{document}

效果如下:
文档效果

xeCJK 宏包是通过 TECKit 生成的映射文件实现这一功能的,我们在路径 %texmf%/fonts/misc/xetex/fontmapping/xeCJK/ 下可以看到对应四个选项的 *.map 文件和 *.tec 文件。其中,宏包需要调用 *.tec 文件,这个文件由 TECKit 编译对应的 *.map 文件而成。我们只需要制作自己的映射文件,放在 TDS 相应位置即可使用 xeCJK 进行调用。

TECKit 制作直角引号的映射文件

TECKit 是一个执行编码转换以供其他应用程序使用的小工具包。可以在官方网站下载页面下载。压缩包下载后解压在任意位置,进入系统架构对应文件夹,其中的 teckit-compile.exe 即为编译命令。

在这里不对 TECKit 进行详细的介绍(目前我只会照猫画虎,没有深研究这个程序,想介绍也没能力)。先复制 %texmf%/fonts/misc/xetex/fontmapping/xeCJK/fullwidth-stop.map 到这个目录,可以看一下 *.map 文件的内容为:

1
2
3
4
5
6
7
8
9
; TECkit mapping for conversion of `IDEOGRAPHIC FULL STOP" to `FULLWIDTH FULL STOP"
;

LHSName "IDEOGRAPHIC FULL STOP"
RHSName "FULLWIDTH FULL STOP"

pass(Unicode)

U+3002 <> U+FF0E

源文件内容猜都很容易猜出来表达的是什么,; 显然是注释符(和 Lisp 有点像),LHSName 是说明左侧的符号类型,RHSName 是说明侧的符号类型,在 pass(Unicode) 之后,需要映射的字符 Unicode 编码之间用 <> 分隔,每一行定义一个字符即可。那么我们可以先把这个文件重命名为 raquot.map,内容改为:

1
2
3
4
5
6
7
8
9
10
11
12
; TECkit mapping for conversion of `Qutation Mark" to `Right Angle Qutation Mark"
;

LHSName "Qutation Mark"
RHSName "Right Angle Qutation Mark"

pass(Unicode)

U+2018 <> U+300C
U+2019 <> U+300D
U+201C <> U+300E
U+201D <> U+300F

保存文件后,在命令行运行:

1
teckit_compile raquot.map -o raquot.tec

文件夹内就编译生成了一个 raquot.tec 文件,我们将这个文件复制到本地 TDS 下(不建议放在系统 TDS 下,升级时恐怕就被删掉了),路径为:%texmf-local%/fonts/misc/xetex/fontmapping/xeCJK/,然后以管理员身份在命令行中运行:

1
texhash

等命令运行完毕后,我们就可以在 LaTeX 文档中使用直角引号的映射了。现在把 MWE 改为:

1
2
3
4
5
6
\documentclass{ctexart}
\usepackage{zhlipsum}
\setCJKmainfont{SimSun}[Mapping=raquot]
\begin{document}
\zhlipsum[1][name=zhufu]
\end{document}

编译文件后,文档中的 “ ” 就输出成为了 『 』,效果为:

文档效果

注意,映射文件的效果不能叠加,如果你既需要将 映射为 ,又需要将 “ ” 映射为 『 』,则需要重新创建一个映射方案,在 *.map 中将这些对应映射放在一起,重新编译一个 *.tec 使用。

其他映射的实现

同理,我们可以创建单书名号的映射方案 bookquot,但是这种映射不能全文使用,毕竟单书名号只是用在篇名中引用书名的情况。这种映射通常只是用于宏命令的定义中,通过 xeCJK 宏包提供的 \addCJKfontfeatures 命令临时增加当前 CJK 字体的选项(对于 Mapping 选项只能是改变,Mapping 的多个方案是不支持叠加的)。例如,我在 Corp 宏集内曾定义了这样一个宏命令 \公文 来支持输出公文的标题、文号等。这个宏命令的定义是:

1
2
3
4
5
\NewDocumentCommand\公文{s O{} O{} O{} m}{
\IfBooleanTF#1
{《{\addCJKfontfeatures{Mapping=bookquot}#5}》}
{《{\addCJKfontfeatures{Mapping=bookquot}#5}》(#2#3#4 号)}
}

这个命令带三个可选参数,一个必选参数,并提供了带 * 命令形式。必选参数是公文的标题,实际排印的时候,会将标题中的书名号映射为单书名号,并将公文标题放在一对书名号 《 》 之间,带 * 命令将省略发文文号,不带 * 的命令则将按可选参数提供的编号给出公文的文号。举例:

1
2
3
\公文[沪水务][2019][1217]{上海市水务局关于印发《上海市排水检查井塑料防坠格板技术规程》的通知}

\公文*{上海市水务局关于印发《上海市排水检查井塑料防坠格板技术规程》的通知}

效果如下:
文档效果

其他映射字符和映射方法,你尽可以充分发挥自己的想象力去实现。Happy LaTeX\LaTeXing!

感谢拨冗阅读本文,若有些许收获,不妨捐赠以资鼓励。