LaTeX 说到底还是一个排版系统,字体是排版关注的核心内容之一,但是由于历史原因,字体系统在设计和使用中混乱不堪, 深究起来就是巨坑,这里简单整理一下 LaTeX 在中文排版时的字体配置相关知识。
由于版权等原因,Tex Live 虽然已经打包了很多内容,但是并不会包括一些常见的中文字体,需要用户自行配置处理。 由于本文中的命令需要直接或间接依赖 fontspec 宏包,它是一个基于 XeLaTeX 和 LuaLaTeX 的字体配置宏包,因此不支持 pdfLaTeX,这个宏包可能会影响数学字体,在使用时可以传入 no-math 选项。 本文只考虑 ctex 宏包,在 Windows/Linux 系统的中文字体排版,并且只使用 XeLaTeX 。
本文主要参考 ctex 用户文档,以及 LaTeX 中文字体配置基础指南
中文字体常识 与书法字体不同,中文印刷/显示字体主要有以下几种:
楷体:源自实际书写中的楷体得到的字体
宋体:从唐宋直到明清,印刷行业对字体从楷体进行的简化,典型的特点是横平竖直,并且有尖角等辅助结构。(与宋朝没什么特别关系,主要定型于明朝,在清朝被改称宋体,在日本被称为明体)
仿宋:因为宋体不美观,在民国时期参考宋朝古籍重新设计的字体,相当于在宋体的基础上向楷体进行的复古潮,例如横平竖直不再是严格的,而是略微倾斜等
黑体:计算机时代,适合显示屏呈现的字体,特点是横竖笔画粗细均匀,并且没有多余的笔画
时至今日,宋体仍然是公文报刊和书籍等的正文部分的主流字体,有很多原因:历史习惯;长时间阅读时,宋体字相对不易疲劳;在字体风格上,宋体代表着文化和传统。 计算机和手机上显示的默认都是黑体:Windows 使用的是著名的微软雅黑(版权是方正的,不可以用来商用),安卓使用的是开源的思源黑体,这也有很多原因:早期显示器分辨率差,宋体等的细节无法呈现,而黑体的可读性更高;在字体风格上,黑体体现着现代和科技感。
在英文字体中,通常会按照衬线和无衬线来分类,区别是核心笔画之外是否含有装饰性的笔画,将概念迁移到中文字体中,宋体就是典型的衬线字体,而黑体是典型的无衬线字体。 英文字体中还有等宽字体的概念,等宽字体保证每一个字母都显示相同的显示宽度,这在代码编写和表格排版等情景中很常见,将概念迁移到中文字体中,通常对应为所谓的等线字体。
在 Windows 系统中,默认会安装并使用如下字体:
黑体:SimHei
宋体(中易宋体):SimSun
新宋体:NSimSun
仿宋:FangSong
楷体:KaiTi
仿宋 GB2312:FangSongGB2312
楷体 GB2312:KaiTiGB2312
微软雅黑:Microsoft YaHei
在安装了 office 之后还会多出一些字体,例如
隶书:LiSu
幼圆:YouYuan
华文系列:华文细黑:STXihei,华文楷体:STKaiti,华文宋体:STSong,华文中宋:STZhongsong,华文仿宋:STFangsong,华文行楷:STXingkai,等等。
方正舒体:FZShuTi,方正姚体:FZYaoti
PDF 中使用到的部分字体通常会直接嵌入到 PDF 中,即使接收方没有安装对应的字体,PDF 的内容也可以正常显示。
ctex 字体配置常识 ctex 是一个统一的中文排版宏包,涉及到默认中文字体,中文标点,中文间空格的处理等排版细节, 它针对不同编译器自动选择不同的底层中文支持方案(CJK,xeCJK,luatexja 等), 针对不同平台会自动选择不同的默认字体,如下图所示(表格取自 ctex 宏包手册)
其中对于系统的判定比较简单:它可以判断出是否是 Windows 或 macOS 系统,对于其它情形全部视作 Linux 系统。 对于编译器只关注 XeLaTeX。
关于中易字库:中易宋体(SimSun.ttc)(属于宋体,衬线体)是中易公司开发的一套字体,在微软雅黑(微软委托方正公司开发)出现之前是 Windows 默认中文字体,但是目前的 Windows 默认字体为微软雅黑(msyh.ttf)(属于黑体,无衬线体)。由于历史原因,Windows 最新系统中仍然会附带中易宋体,并且在 Windows 简体中文环境下会直接显示为“宋体”,这很容易导致歧义。
关于 fandol 字库 :这是一套由马起园等人开发的开源中文字体。由于开发字体的公司已经破产,因此字体的商用版权存在争议。在 Linux 系统被默认使用,但是 fandol 字体的效果和完整性并不好,例如它缺少浛,祎这些不太生僻的姓名常用字,在编译后的 PDF 中会显示为方框,并给出警告。
查看当前 texlive 可以识别的所有中文字体(在 Linux 和 Windows 均可以使用)
1 fc-list -f "%{family}\n" :lang=zh > zhfont.txt
可用如下命令刷新 texlive 的字体缓存(在 Linux 和 Windows 均可以使用,可能需要sudo)
ctex 在内部定义了一系列字体配置文件,位于 TexLive 安装根目录中的texmf-dist/tex/latex/ctex/fontset子目录,其下有八个字体定义文件,对应了不同的字体预设方案:
ctex-fontset-adobe.def (adobe 字体方案)
ctex-fontset-fandol.def
ctex-fontset-founder.def (方正 founder 字体方案)
ctex-fontset-mac.def
ctex-fontset-macnew.def
ctex-fontset-macold.def
ctex-fontset-ubuntu.def
ctex-fontset-windows.def
在导入 ctex 宏包或者 ctexart 文档类时可以使用选项fontset=windows来指定选择其中一种预设方案,例如
1 2 3 4 5 6 7 \documentclass [fontset=windows]{ctexart}... \documentclass {article}\usepackage [fontset=windows]{ctex}...
其中:
如果 fontset 选项缺省,ctex 就会根据当前的系统来判断,通常会默认选择 windows 或 fandol 方案。
如果拒绝使用这些默认方案,可以指定fontset=none,然后自行配置中文字体方案,具体命令见下文。
即使采用了这些默认方案,仍然可以用下面的自定义命令来改变使用的字体。
ctex 默认配置解析 对几个默认方案进行解析,移除了注释后的源文件全文如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 \GetIdInfo $ Id: ctex.dtx 13a2256 2022-07-14 18:54:09 +0800 Qing Lee <sobenlee@gmail.com> $ {Windows fonts definition (CTEX)} \ProvidesExplFile {ctex-fontset-windows.def} {\ExplFileDate }{2.5.10}{\ExplFileDescription } \file_if_exist:nTF { \c_dollar_str WINDIR/Fonts/msyh.ttc } { \tl_const:Nn \c__ctex_msyh_suffix_tl { ttc } } { \file_if_exist:nTF { msyh.ttc } { \tl_const:Nn \c__ctex_msyh_suffix_tl { ttc } } { \tl_const:Nn \c__ctex_msyh_suffix_tl { ttf } } } \ctex_fontset_case:nnn { \ctex_zhmap_case:nnn { \ctex_punct_set:n { windows } \setCJKmainfont { simsun.ttc } [ BoldFont = simhei.ttf, ItalicFont = simkai.ttf ] \setCJKsansfont { msyh.\c__ctex_msyh_suffix_tl } [ BoldFont = msyhbd.\c__ctex_msyh_suffix_tl ] \setCJKmonofont { simfang.ttf } \setCJKfamilyfont { zhsong } { simsun.ttc } \setCJKfamilyfont { zhhei } { simhei.ttf } \setCJKfamilyfont { zhfs } { simfang.ttf } \setCJKfamilyfont { zhkai } { simkai.ttf } \setCJKfamilyfont { zhyahei } { msyh.\c__ctex_msyh_suffix_tl } [ BoldFont = msyhbd.\c__ctex_msyh_suffix_tl ] \setCJKfamilyfont { zhli } { simli.ttf } \setCJKfamilyfont { zhyou } { simyou.ttf } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_bfseries:nn { \CJKrmdefault } { zhhei } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } \ctex_punct_map_family:nn { \CJKsfdefault } { zhyahei } \ctex_punct_map_bfseries:nn { \CJKsfdefault , zhyahei } { zhyaheib } \ctex_punct_map_family:nn { \CJKttdefault } { zhfs } } { \ctex_load_zhmap:nnnn { rm } { zhhei } { zhfs } { windows } \ctex_punct_set:n { windows } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_bfseries:nn { \CJKrmdefault } { zhhei } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } } { } } { \ctex_set_upfonts:nnnnnn { simsun.ttc } { simhei.ttf } { simkai.ttf } { msyh.\c__ctex_msyh_suffix_tl } { msyhbd.\c__ctex_msyh_suffix_tl } { simfang.ttf } \ctex_set_upfamily:nnn { zhsong } { upzhserif } {} \ctex_set_upfamily:nnn { zhhei } { upzhserifb } {} \ctex_set_upfamily:nnn { zhfs } { upzhmono } {} \ctex_set_upfamily:nnn { zhkai } { upzhserifit } {} \ctex_set_upfamily:nnn { zhyahei } { upzhsans } { upzhsansb } \ctex_set_upfamily:nnn { zhli } { upschrm } {} \ctex_set_upfamily:nnn { zhyou } { upschgt } {} \ctex_set_upmap:nnn { upstsl } { simli.ttf } {} \ctex_set_upmap:nnn { upstht } { simyou.ttf } {} } { \setCJKmainfont { SimSun } [ BoldFont = SimHei , ItalicFont = KaiTi ] \setCJKsansfont { Microsoft~YaHei } [ BoldFont = *~Bold ] \setCJKmonofont { FangSong } \setCJKfamilyfont { zhsong } { SimSun } \setCJKfamilyfont { zhhei } { SimHei } \setCJKfamilyfont { zhfs } { FangSong } \setCJKfamilyfont { zhkai } { KaiTi } \setCJKfamilyfont { zhyahei } { Microsoft~YaHei } [ BoldFont = *~Bold ] \setCJKfamilyfont { zhli } { LiSu } \setCJKfamilyfont { zhyou } { YouYuan } } \NewDocumentCommand \songti { } { \CJKfamily { zhsong } }\NewDocumentCommand \heiti { } { \CJKfamily { zhhei } }\NewDocumentCommand \fangsong { } { \CJKfamily { zhfs } }\NewDocumentCommand \kaishu { } { \CJKfamily { zhkai } }\NewDocumentCommand \lishu { } { \CJKfamily { zhli } }\NewDocumentCommand \youyuan { } { \CJKfamily { zhyou } }\NewDocumentCommand \yahei { } { \CJKfamily { zhyahei } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 \GetIdInfo $ Id: ctex.dtx 13a2256 2022-07-14 18:54:09 +0800 Qing Lee <sobenlee@gmail.com> $ {Fandol fonts definition (CTEX)} \ProvidesExplFile {ctex-fontset-fandol.def} {\ExplFileDate }{2.5.10}{\ExplFileDescription } \ctex_fontset_case:nnnn { \ctex_fontset_error:n { fandol } } { \ctex_zhmap_case:nnn { \setCJKmainfont { FandolSong-Regular.otf } [ cmap = UniGB-UTF16-H, BoldFont = FandolSong-Bold.otf, ItalicFont = FandolKai-Regular.otf ] \setCJKsansfont { FandolHei-Regular.otf } [ cmap = UniGB-UTF16-H, BoldFont = FandolHei-Bold.otf ] \setCJKmonofont { FandolFang-Regular.otf } [ cmap = UniGB-UTF16-H ] \setCJKfamilyfont { zhsong } { FandolSong-Regular.otf } [ cmap = UniGB-UTF16-H, BoldFont = FandolSong-Bold.otf ] \setCJKfamilyfont { zhhei } { FandolHei-Regular.otf } [ cmap = UniGB-UTF16-H, BoldFont = FandolHei-Bold.otf ] \setCJKfamilyfont { zhfs } { FandolFang-Regular.otf } [ cmap = UniGB-UTF16-H ] \setCJKfamilyfont { zhkai } { FandolKai-Regular.otf } [ cmap = UniGB-UTF16-H ] \ctex_punct_set:n { fandol } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_family:nn { \CJKsfdefault } { zhhei } \ctex_punct_map_family:nn { \CJKttdefault } { zhfs } \ctex_punct_map_bfseries:nn { \CJKrmdefault , zhsong } { zhsongb } \ctex_punct_map_bfseries:nn { \CJKsfdefault , zhhei } { zhheib } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } } { \ctex_load_zhmap:nnnn { rm } { zhhei } { zhfs } { fandol } \ctex_punct_set:n { fandol } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_bfseries:nn { \CJKrmdefault } { zhhei } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } } { \ctex_fontset_error:n { fandol } } } { \ctex_set_upfonts:nnnnnn { FandolSong-Regular.otf } { FandolSong-Bold.otf } { FandolKai-Regular.otf } { FandolHei-Regular.otf } { FandolHei-Bold.otf } { FandolFang-Regular.otf } \ctex_set_upfamily:nnn { zhsong } { upzhserif } { upzhserifb } \ctex_set_upfamily:nnn { zhhei } { upzhsans } { upzhsansb } \ctex_set_upfamily:nnn { zhfs } { upzhmono } {} \ctex_set_upfamily:nnn { zhkai } { upzhserifit } {} } { \setCJKmainfont { FandolSong-Regular } [ Extension = .otf, BoldFont = FandolSong-Bold, ItalicFont = FandolKai-Regular ] \setCJKsansfont { FandolHei-Regular } [ Extension = .otf, BoldFont = FandolHei-Bold ] \setCJKmonofont { FandolFang-Regular } [ Extension = .otf ] \setCJKfamilyfont { zhsong } { FandolSong-Regular } [ Extension = .otf, BoldFont = FandolSong-Bold ] \setCJKfamilyfont { zhhei } { FandolHei-Regular } [ Extension = .otf, BoldFont = FandolHei-Bold ] \setCJKfamilyfont { zhfs } { FandolFang-Regular } [ Extension = .otf ] \setCJKfamilyfont { zhkai } { FandolKai-Regular } [ Extension = .otf ] } \NewDocumentCommand \songti { } { \CJKfamily { zhsong } }\NewDocumentCommand \heiti { } { \CJKfamily { zhhei } }\NewDocumentCommand \fangsong { } { \CJKfamily { zhfs } }\NewDocumentCommand \kaishu { } { \CJKfamily { zhkai } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 \GetIdInfo $ Id: ctex.dtx 13a2256 2022-07-14 18:54:09 +0800 Qing Lee <sobenlee@gmail.com> $ {Founder fonts definition (CTEX)} \ProvidesExplFile {ctex-fontset-founder.def} {\ExplFileDate }{2.5.10}{\ExplFileDescription } \ctex_fontset_case:nnn { \ctex_zhmap_case:nnn { \setCJKmainfont { FZSSK.TTF } [ BoldFont = FZXBSK.TTF, ItalicFont = FZKTK.TTF ] \setCJKsansfont { FZXH1K.TTF } [ BoldFont = FZHTK.TTF ] \setCJKmonofont { FZFSK.TTF } \setCJKfamilyfont { zhsong } { FZSSK.TTF } [ BoldFont = FZXBSK.TTF ] \setCJKfamilyfont { zhhei } { FZHTK.TTF } \setCJKfamilyfont { zhkai } { FZKTK.TTF } \setCJKfamilyfont { zhfs } { FZFSK.TTF } \setCJKfamilyfont { zhli } { FZLSK.TTF } \setCJKfamilyfont { zhyou } { FZY1K.TTF } [ BoldFont = FZY3K.TTF ] \ctex_punct_set:n { founder } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_family:nn { \CJKsfdefault } { zhheil } \ctex_punct_map_family:nn { \CJKttdefault } { zhfs } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } \ctex_punct_map_bfseries:nn { \CJKrmdefault , zhsong } { zhsongb } \ctex_punct_map_bfseries:nn { \CJKsfdefault } { zhhei } \ctex_punct_map_bfseries:nn { zhyou } { zhyoub } } { \ctex_load_zhmap:nnnn { rm } { zhhei } { zhfs } { founder } \ctex_punct_set:n { founder } \ctex_punct_map_family:nn { \CJKrmdefault } { zhsong } \ctex_punct_map_bfseries:nn { \CJKrmdefault } { zhhei } \ctex_punct_map_itshape:nn { \CJKrmdefault } { zhkai } } { \ctex_fontset_error:n { founder } } } { \ctex_set_upfonts:nnnnnn { FZSSK.TTF } { FZXBSK.TTF } { FZKTK.TTF } { FZXH1K.TTF } { FZHTK.TTF } { FZFSK.TTF } \ctex_set_upfamily:nnn { zhsong } { upzhserif } { upzhserifb } \ctex_set_upfamily:nnn { zhhei } { upzhsans } { upzhsansb } \ctex_set_upfamily:nnn { zhfs } { upzhmono } {} \ctex_set_upfamily:nnn { zhkai } { upzhserifit } {} \ctex_set_upfamily:nnn { zhli } { upschrm } {} \ctex_set_upfamily:nnn { zhyou } { upschgt } {} \ctex_set_upmap:nnn { upstsl } { FZLSK.TTF } {} \ctex_set_upmap:nnn { upstht } { FZY1K.TTF } {} } { \setCJKmainfont { FZShuSong-Z01 } [ BoldFont = FZXiaoBiaoSong-B05, ItalicFont = FZKai-Z03 ] \setCJKsansfont { FZXiHeiI-Z08 } [ BoldFont = FZHei-B01 ] \setCJKmonofont { FZFangSong-Z02 } \setCJKfamilyfont { zhsong } { FZShuSong-Z01 } [ BoldFont = FZXiaoBiaoSong-B05 ] \setCJKfamilyfont { zhhei } { FZHei-B01 } \setCJKfamilyfont { zhkai } { FZKai-Z03 } \setCJKfamilyfont { zhfs } { FZFangSong-Z02 } \setCJKfamilyfont { zhli } { FZLiShu-S01 } \setCJKfamilyfont { zhyou } { FZXiYuan-M01 } [ BoldFont = FZZhunYuan-M02 ] } \NewDocumentCommand \songti { } { \CJKfamily { zhsong } }\NewDocumentCommand \heiti { } { \CJKfamily { zhhei } }\NewDocumentCommand \fangsong { } { \CJKfamily { zhfs } }\NewDocumentCommand \kaishu { } { \CJKfamily { zhkai } }\NewDocumentCommand \lishu { } { \CJKfamily { zhli } }\NewDocumentCommand \youyuan { } { \CJKfamily { zhyou } }
这些默认配置方案的核心部分其实只有三行代码(剩下的内容是处理标点等排版细节,并且定义了一些字体族和命令,例如\songti等,便于调整局部的字体)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 \setCJKmainfont { simsun.ttc }[ BoldFont = simhei.ttf, ItalicFont = simkai.ttf ]\setCJKsansfont { msyh.\c__ctex_msyh_suffix_tl }[ BoldFont = msyhbd.\c__ctex_msyh_suffix_tl ]\setCJKmonofont { simfang.ttf }\setCJKmainfont { FandolSong-Regular.otf }[ cmap = UniGB-UTF16-H,BoldFont = FandolSong-Bold.otf,ItalicFont = FandolKai-Regular.otf ]\setCJKsansfont { FandolHei-Regular.otf }[ cmap = UniGB-UTF16-H, BoldFont = FandolHei-Bold.otf ]\setCJKmonofont { FandolFang-Regular.otf }[ cmap = UniGB-UTF16-H ]\setCJKmainfont { FZShuSong-Z01 }[ BoldFont = FZXiaoBiaoSong-B05, ItalicFont = FZKai-Z03 ]\setCJKsansfont { FZXiHeiI-Z08 } [ BoldFont = FZHei-B01 ]\setCJKmonofont { FZFangSong-Z02 }
值得注意的是:
中文字体通常不会附带自身的粗体或斜体变式,因此在 windows 字体方案中,采用黑体作为宋体的粗体,这是比较奇怪的行为,因为宋体加粗后应该是“加粗的宋体”才合适,就像 fandol 字体方案一样。普遍采用楷体作为宋体的斜体。
fandol 字体方案经常会出现如下警告,这种警告通常是不影响正常编译的,除了看起来比较烦人之外没什么影响
1 2 Font "FandolSong-Regular" does not contain requested (fontspec) Script "CJK".
ctex 字体配置命令 通过对默认字体方案的解析可知,ctex 中文字体配置的核心为如下三条命令(这几个命令实际是 xeCJK 宏包提供的)
1 2 3 4 5 6 \setCJKmainfont {<font name>}[<font features>]\setCJKsansfont {<font name>}[<font features>]\setCJKmonofont {<font name>}[<font features>]
其中必选项为字体名称,可选项通常用来指定对应的粗体和斜体版本。(旧版本的可选项位置在必选项之前,最新版本更改了顺序如上,但是仍然兼容旧版本的写法)
通常还会附带定义一些字体族
1 2 3 4 5 6 7 \setCJKfamilyfont { zhsong } { SimSun }\setCJKfamilyfont { zhhei } { SimHei }\setCJKfamilyfont { zhfs } { FangSong }\setCJKfamilyfont { zhkai } { KaiTi }\setCJKfamilyfont { zhyahei } { Microsoft~YaHei } [ BoldFont = *~Bold ]\setCJKfamilyfont { zhli } { LiSu }\setCJKfamilyfont { zhyou } { YouYuan }
此时可以使用下面的命令,这里加上括号可以限制作用范围,否则设置一直有效,直到下一次改变为止。
1 {\CJKfamily {zhkai} 中文字体(楷书)}
进一步的,还会将其封装为自定义命令(两种方式均可,第一种方式是 ctex 采用的,命令由 xparse 宏包提供)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 \NewDocumentCommand \songti { } { \CJKfamily { zhsong } }\NewDocumentCommand \heiti { } { \CJKfamily { zhhei } }\NewDocumentCommand \fangsong { } { \CJKfamily { zhfs } }\NewDocumentCommand \kaishu { } { \CJKfamily { zhkai } }\NewDocumentCommand \lishu { } { \CJKfamily { zhli } }\NewDocumentCommand \youyuan { } { \CJKfamily { zhyou } }\NewDocumentCommand \yahei { } { \CJKfamily { zhyahei } }\newcommand *{ \songti }{ \CJKfamily { zhsong }}\newcommand *{ \heiti }{ \CJKfamily { zhhei }}\newcommand *{ \kaishu }{ \CJKfamily { zhkai }}\newcommand *{ \fangsong }{ \CJKfamily { zhfs }}...
此时就可以使用\kaishu等命令更加便捷地切换局部的字体,例如
1 2 3 {\kaishu 这是楷体。} {\fangsong 这是仿宋。}
下面的自定义配置方案会保证\songti,\heiti,\kaishu和\fangsong这四个最基础的字体命令始终可以直接使用。
自定义字体配置 ctex 采用的默认中文字体要么比较垃圾(例如 fandol),要么不是完全开源的(例如中易宋体),甚至微软雅黑也不能商用, 因此在自行配置字体时普遍会选择设计更加优秀的,并且完全开源的字体(对于 Windows 必须将字体全局安装),主流的是以下两个方案:
思源字体系列(完全开源)
方正字体系列(仅有四个开源字体)
方正字体 方正仅有的四个开源字体如下,注意直接在官网下载的话,需要选择 GBK 编码的文件:
方正书宋:FZSSK.TTF,FZShuSong-Z01
方正黑体:FZHTK.TTF,FZHei-B01
方正楷体:FZKTK.TTF,FZKai-Z03
方正仿宋:FZFSK.TTF,FZFangSong-Z02
注意到 ctex 默认的 founder 方正字体方案中,并不仅仅是这几种字体,还有额外的非开源方正字体,并且在 Windows 系统上通常不会默认安装,因此在 Windows 上直接使用 fontset=founder 会因为缺字体而编译失败。
在安装了四种开源字体之后,方正开源字体配置如下(与 ctex 默认的 founder 方案相似但不一样,使用了更少的方正字体)
1 2 3 4 5 6 7 8 9 10 11 12 13 \setCJKmainfont {FZShuSong-Z01}[BoldFont={FZHei-B01},ItalicFont={FZKai-Z03}]\setCJKsansfont {FZHei-B01}\setCJKmonofont {FZFangSong-Z02}\setCJKfamilyfont {zhsong}{FZShuSong-Z01}\setCJKfamilyfont {zhhei}{FZHei-B01}\setCJKfamilyfont {zhkai}{FZKai-Z03}\setCJKfamilyfont {zhfs}{FZFangSong-Z02}\newcommand *{\songti }{\CJKfamily {zhsong}}\newcommand *{\heiti }{\CJKfamily {zhhei}}\newcommand *{\kaishu }{\CJKfamily {zhkai}}\newcommand *{\fangsong }{\CJKfamily {zhfs}}
另一种方正开源字体配置如下(参考 ElegantLaTeX),更改了无衬线字体为楷书,并设置了相应的粗体
1 2 3 4 5 6 7 8 9 10 11 12 13 \setCJKmainfont {FZShuSong-Z01}[BoldFont={FZHei-B01},ItalicFont={FZKai-Z03}]\setCJKsansfont [BoldFont={FZHei-B01}]{FZKai-Z03}\setCJKmonofont [BoldFont={FZHei-B01}]{FZFangSong-Z02}\setCJKfamilyfont {zhsong}{FZShuSong-Z01}\setCJKfamilyfont {zhhei}{FZHei-B01}\setCJKfamilyfont {zhkai}{FZKai-Z03}[BoldFont={FZHei-B01}]\setCJKfamilyfont {zhfs}{FZFangSong-Z02}[BoldFont={FZHei-B01}]\newcommand *{\songti }{\CJKfamily {zhsong}}\newcommand *{\heiti }{\CJKfamily {zhhei}}\newcommand *{\kaishu }{\CJKfamily {zhkai}}\newcommand *{\fangsong }{\CJKfamily {zhfs}}
思源字体 思源字体由 Adobe 和 Google 联合开发,包括思源宋体 和思源黑体 两款字体,各有七个字重:ExtraLight、Light、Normal、Regular、Medium、Bold 和 Heavy。
Adobe 将其命名为思源字体,英文名分别为Source Han Serif和Source Han Sans,字体仓库分别为 Source Han Serif 和 Source Han Sans 。
Google 则将其命名为 Noto CJK 字体,英文名分别为Noto Serif CJK和Noto Sans CJK,字体仓库为 Noto CJK 。除了思源宋体和思源黑体,Noto CJK 系列还有一套等宽的中文字体(只有两个字重),名为Noto Sans Mono CJK。
这一系列字体文件的名称非常繁杂(因为是两家合作,并且需要考虑不同语言和地区习惯),可能使用如下的缩写表示:
语言特定版本 (Language-specific Fonts)
SC - 简体中文
TC - 繁体中文—台湾地区写法标准
HC - 繁体中文—香港地区写法标准
J - 日文
K - 韩文
地区子集版本 (Region-specific Subset Fonts)
CN - 中国大陆
TW - 台湾地区
HK - 香港地区
JP - 日本
KR - 韩国
当前的个人选择的是将简体中文 SC 后缀的两个字体各两个版本都下载安装到本地,每一个都包括 7 个不同字重的。otf 文件,再加上一套等宽字体。 下载链接见各自仓库的 Release 页面
思源宋体一 Source Han Serif SC
思源黑体一 Source Han Sans SC
思源宋体二 Noto Serif CJK SC
思源黑体二 Noto Sans CJK SC
思源等宽字体 Noto Sans Mono CJK SC
使用思源字体时,很可能因为字体名称的异常导致编译失败,因为这套开源字体的名称过于复杂了,安装的字体和使用的字体可能后缀差了一点点,比如 SC 和 CN 分别代表简体中文和中国大陆,两者字体几乎一样,但是严格来说是不一样的字体。 按道理来说,应该有一个名为 Source Han Mono 的等宽字体与 Noto Sans Mono 对应,但是 Adobe 并没有发布单独的版本,只有一个没有维护的 Github 仓库,里面是所有字体的大杂烩,而且需要自行编译。
配置例如
1 2 3 \setCJKmainfont {Noto Serif CJK SC}\setCJKsansfont {Noto Sans CJK SC}\setCJKmonofont {Noto Sans Mono CJK SC}
注意这里没有字体命令例如\kaishu等,因为思源字体只有宋体和黑体以及等线体,并且有完整的字重分类。
参考方案 参考方案一(混合使用方正和思源字体)
1 2 3 4 5 6 \setCJKmainfont {FZShuSong-Z01}[BoldFont={Source Han Serif SC SemiBold}, ItalicFont=FZKai-Z03]\setCJKsansfont {FZHei-B01}[BoldFont={Source Han Sans SC Medium}]\setCJKmonofont {FZFangSong-Z02}
参考方案二(ustcthesis)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 \setCJKmainfont {Noto Serif CJK SC}[ UprightFont = * Light, BoldFont = * Bold, ItalicFont = FandolKai-Regular, ItalicFeatures = {Extension = .otf}, Script = CJK, ] \setCJKsansfont {Noto Sans CJK SC}[ BoldFont = * Medium, Script = CJK, ] \setCJKmonofont {Noto Sans Mono CJK SC}[ Script = CJK, ] \setCJKfamilyfont {zhsong}{Noto Serif CJK SC}[ UprightFont = * Light, UprightFont = * Bold, Script = CJK, ] \setCJKfamilyfont {zhhei}{Noto Sans CJK SC}[ BoldFont = * Medium, Script = CJK, ] \setCJKfamilyfont {zhfs}{FandolFang}[ Extension = .otf, UprightFont = *-Regular, ] \setCJKfamilyfont {zhkai}{FandolKai}[ Extension = .otf, UprightFont = *-Regular, ]
参考方案三(LaTeX 工作室)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 \setCJKmainfont {Source Han Serif CN}[ UprightFont = *-Regular, BoldFont = *-Bold, ItalicFont = *-Regular, BoldItalicFont = *-Bold ] \setCJKsansfont {Noto Sans CJK SC}[ UprightFont = *-Regular, BoldFont = *-Bold, ItalicFont = *-Regular, BoldItalicFont = *-Bold ] \setCJKmonofont {Noto Sans Mono CJK SC}[ UprightFont = *-Regular, BoldFont = *-Bold, AutoFakeSlant = 0.2 ] \setCJKfamilyfont {zhsong}{Source Han Serif CN}[ UprightFont = *-Regular, BoldFont = *-Bold, ItalicFont = *-Regular, BoldItalicFont = *-Bold ] \setCJKfamilyfont {zhhei}{Noto Sans CJK SC}[ UprightFont = *-Regular, BoldFont = *-Bold, ItalicFont = *-Regular, BoldItalicFont = *-Bold ] \setCJKfamilyfont {zhxh}{Noto Sans CJK SC Light}
自定义方案 记录一下我自己使用的几个方案,只是定义了最核心的三个字体,不包括其它字体命令的定义,第一个纯方正的方案在 beamer 上的效果也挺好。
FZ 1 2 3 \setCJKmainfont {FZShuSong-Z01}[BoldFont={FZHei-B01},ItalicFont={FZKai-Z03}]\setCJKsansfont {FZKai-Z03}[BoldFont={FZHei-B01}]\setCJKmonofont {FZFangSong-Z02}[BoldFont={FZHei-B01}]
NotoCJK 1 2 3 4 5 6 7 8 9 10 11 12 13 \setCJKmainfont {Noto Serif CJK SC}[ UprightFont = * Light, BoldFont = * Bold, ItalicFont = FZKai-Z03, Script = CJK, ] \setCJKsansfont {Noto Sans CJK SC}[ BoldFont = * Medium, Script = CJK, ] \setCJKmonofont {Noto Sans Mono CJK SC}[ Script = CJK, ]
SourceHan 1 2 3 4 5 6 7 8 9 10 11 \setCJKmainfont {Source Han Serif SC}[ UprightFont = * Light, BoldFont = * Bold, ItalicFont = FZKai-Z03, Script = CJK, ] \setCJKsansfont {Source Han Sans SC}[ BoldFont = * Medium, Script = CJK, ] \setCJKmonofont {FZFangSong-Z02}
关于字体报错 记录一下遇到的字体报错以及解决办法。
LaTeX 在遇到字符缺失时默认并不会直接中止,而是会使用空白字符进行替换,并继续编译,在最后的警告信息也不会告诉我们具体出错的位置,这导致很难问题难以定位和处理。
我们可以手动修改 \tracinglostchars 以调整丢失字符的提示级别,默认级别为 1,产生简单的警告信息, 修改为 3,则会导致致命错误,立刻终止编译,并显示具体出错的位置。 在文档的开始部分加上下面的命令即可
中文字体缺少常见字 在排版中文文档时,有时会遇到如下字体错误:
1 2 Missing character: There is no ⾄ (U+2F84) in font SimSun/OT:script=hani;language=dflt;. <XeTeXinterchartoks>
这类报错通常是因为文档中包含了某些字形与常用汉字相似但 Unicode 编码不同的字符,这些字符并不被当前使用的中文字体支持,从而导致编译失败或输出空白。
例如下面两组字符是不一样的,肉眼可能难以区分(取决于编辑器和浏览器使用的字体)
正常字符(常用汉字)
Unicode
异常字符(康熙部首等)
Unicode
立
U+7ACB
⽴
U+2F74
网
U+7F51
⽹
U+2F79
至
U+81F3
⾄
U+2F84
可以直接复制下面的片段到文本编辑器中进行检查,会发现非常难以区分
这些生僻的字符通常不会由用户通过中文输入法输入,常见来源包括:
从 PDF 中直接选中文本复制
使用 OCR 软件自动提取文本
注:
这不是中文字符特有的问题,而是 Unicode 字符集追求大而全带来的必然结果,例如西里尔字母 а(U+0430)和拉丁字母 a(U+0061)形状一模一样,却有不同的 Unicode 编码,此外还有各种横线/破折号等非常相似的 Unicode 字符。
在 LaTeX 排版中遇到这些易混淆的 Unicode 字符只是影响排版,更危险的情况其实是在 URL 中,可能被用于制作钓鱼域名和链接。
数学字体中的中文逗号 在使用中文时,可能会遇到如下警告
1 Missing character: There is no , (U+FF0C) in font [lmroman7-regular]:mapping=tex-text;!
这个警告是关于中文全角的,我们在正文中使用中文全角是不会触发这个警告的,但是在某些特殊的环境中可能出现,需要重点排查数学环境中是否误用了中文全角的逗号,例如