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

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

1
fc-cache -fsv

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}
...

% or
\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
%% ctex-fontset-windows.def

\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
%% ctex-fontset-fandol.def

\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
%% ctex-fontset-founder.def

\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
%% ctex-fontset-windows.def
% 中文默认字体:(中易)宋体,粗体以黑体代替,斜体以楷书代替
\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 }

%% ctex-fontset-fandol.def
% 中文默认字体:Fandol 宋体,粗体为对应的粗体,斜体为 Fandol 楷体
\setCJKmainfont { FandolSong-Regular.otf }[ cmap = UniGB-UTF16-H,BoldFont = FandolSong-Bold.otf,ItalicFont = FandolKai-Regular.otf ]
% 中文无衬线字体:Fandol 黑体,粗体为对应的粗体
\setCJKsansfont { FandolHei-Regular.otf }[ cmap = UniGB-UTF16-H, BoldFont = FandolHei-Bold.otf ]
% 中文等宽字体:Fandol 仿宋
\setCJKmonofont { FandolFang-Regular.otf }[ cmap = UniGB-UTF16-H ]

%% ctex-fontset-founder.def
% 中文默认字体:方正书宋_GBK(FZSSK.TTF),粗体为方正小标宋_GBK(FZXBSK.TTF),斜体为方正楷体_GBK(FZKTK.TTF)
\setCJKmainfont { FZShuSong-Z01 }[ BoldFont = FZXiaoBiaoSong-B05, ItalicFont = FZKai-Z03 ]
% 中文无衬线字体:方正细黑一_GBK(FZXH1K.TTF),粗体为方正黑体_GBK(FZHTK.TTF)
\setCJKsansfont { FZXiHeiI-Z08 } [ BoldFont = FZHei-B01 ]
% 中文等宽字体:方正仿宋_GBK(FZFSK.TTF)
\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 } }

% or
\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 SerifSource Han Sans,字体仓库分别为Source Han SerifSource Han Sans

Google则将其命名为Noto CJK字体,英文名分别为Noto Serif CJKNoto 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
% 中文默认字体:方正书宋_GBK,粗体为思源宋体半粗体,斜体为方正楷体_GBK
\setCJKmainfont{FZShuSong-Z01}[BoldFont={Source Han Serif SC SemiBold}, ItalicFont=FZKai-Z03]
% 中文无衬线字体:方正黑体_GBK,粗体为思源黑体中粗体
\setCJKsansfont{FZHei-B01}[BoldFont={Source Han Sans SC Medium}]
% 中文等宽字体:方正仿宋_GBK
\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
% 注意 Noto CJK 的 regular 字重名字不带“Regular”。

\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} % 用方正字体是因为没有对应的Mono字体

英文正文字体

在LaTeX的article等基本文档类中,默认的英文字体为Computer Modern Roman。

一个常见的英文字体为Times New Roman,在很长时间内它是 Windows 平台上 Word 的默认西文字体,与LaTeX的英文字体不同,并且该字体不能免费商用。

英文字体的配置命令包括如下三条:

1
2
3
4
5
6
% 英文默认字体
\setmainfont{<font name>}[<font features>]
% 英文无衬线字体
\setsansfont{<font name>}[<font features>]
% 英文等宽字体
\setmonofont{<font name>}[<font features>]

例如

1
2
3
\setmainfont{Noto Serif Light}
\setsansfont{Noto Sans}
\setmonofont[Scale=MatchLowercase]{Noto Mono}

再例如

1
2
3
\setmainfont{TeX Gyre Termes}
\usepackage{unicode-math} % 需要 unicode-math 宏包
\setmathfont{TeX Gyre Termes Math}

这个还顺便改变了数学字体。

有的人喜欢花里胡哨的英文字体,例如calligra,需要导入同名的package,例如{\Huge\calligra fenglielie}的效果为

关于英文字体的配置只是记录一下,我并不打算改变默认的英文和数学字体,看着挺顺眼的。

英文编程字体

另外补充记录一下在编程中经常使用的英文字体,对编程字体的常见要求包括:字体等宽,辨识度高(数字0和大小写字母Oo,大写I,数字1,大小写字母Ll容易区分)。

常见的英文编程字体包括:

  • Courier New:(Windows自带)Windows的缺省等宽字体,简称CN,这个字体在Windows早期就存在,因此是很多软件的默认字体,但是它的字形太细了,不适合现在的显示屏和审美
  • Consolas:(Windows自带)相当于是Courier New的升级字体,与CN相比字形更窄,能够在较少的空间显示更多的内容。Consolas是VS系列的默认字体,也是VSCode的默认字体
  • Cascadia:(Windows自带前两个)相当于为新的windows terminal配套提供的一套字体,包括几个版本
    • Cascadia Code 标准版,支持连字,例如箭头->和大于等于号>=
    • Cascadia Mono 不支持连字的版本,Windows在终端默认使用的等宽字体
    • Cascadia (Code|Mono) PL 是支持 PowerLine 符号的版本,Windows不会默认安装
  • Monaco:苹果出品的号称最好的编程字体
  • Source Code Pro:Adobe开发的字体,与Source Hans CJK风格相似
  • Noto Mono:Google开发的一整套集齐各种语言的等宽字体,完整版很大
  • Fira Code:Mozilla 委托设计的开源字体族Fira的扩展,支持连字,经常被网上推荐使用
  • Nerd Fonts:这是一组字体,特点是将常见的图标例如Font Awesome结合到字体中,因此可以在命令行支持各种炫酷主题的显示效果,而一般的字体可能出现缺失图标无法正常显示的问题。其中经常被推荐的字体是Meslo Nerd Font,在终端可以选择MesloLGM Nerd Font作为主要的英文字体

注意,是否显示连字,除了字体本身是否支持,还和编辑器的选项有关,例如VSCode有相关选项可以禁止连字。