文章

TeX 中文字体排忧解难 (macOS)

在 TeX 中配置中文字体的一些个人心得

自从升级 macOS 15 Sequoia 之后,我一直无法用 CTeX 宏集正确地找到系统字体。我平时使用 XeTeX 编译(自己魔改后的)SJTU Beamer 模板,但是默认设置下无法找到楷体、黑体、苹方等等字体。之前妥协于此问题,只能暂时用 fontset=none\setCJKmainfont 来手动指定字体文件。前不久终于在群内大佬的帮助下找到了问题所在,于是在这里记录一下我解决问题的方法。

下面列举一下我的 TeX 环境,如果和你完全一致,那么这篇博客很可能会帮到你:

  • macOS 15.3.1 Sequoia
  • TeXLive 2024, 3.141592653, distributed by Nixpkgs
  • 主要使用 XeTeX

TeX 在哪儿找到字体?

首先要搞清楚 XeTeX 引擎寻找字体的方法:默认配置下,XeTeX 在 Windows 和 GNU/Linux 上使用 fontconfig 找到字体,在 macOS 上使用 Apple 的应用服务 Core Text 找到字体。Nixpkgs 版本的 TeXLive 发行版通过修改配置参数,强制在 macOS 平台上也使用 fontconfig 来找到字体。

1
2
3
4
5
# force XeTeX to use fontconfig instead of Core Text, so that fonts can be made available via FONTCONFIG_FILE,
# by tricking configure into thinking that the relevant test result is already in the config cache
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
    export kpse_cv_have_ApplicationServices=no
'';

这里的配置项与 TeXLive 发行版的源码 对应。Nixpkgs 同样给大多数 TeXLive 安装的可执行文件添加了 wrapper,预设好了 FONTCONFIG_FILE 的值:

1
fontconfigFile = makeFontsConf { fontDirectories = [ "${texmfroot}/texmf-dist/fonts" ]; };

查看本地的 FONTCONFIG_FILE,观察到文件末尾有以下路径:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<cachedir prefix="xdg">fontconfig</cachedir>
<cachedir>/var/cache/fontconfig</cachedir>
<include ignore_missing="yes">/etc/fonts/conf.d</include>
<dir prefix="xdg">fonts</dir>
<dir>/nix/store/1avng232chkhpjldwal6vq4vyzdwpnz1-texlive-2024-env-texmfroot/texmf-dist/fonts</dir>
<dir>~/.nix-profile/lib/X11/fonts</dir>
<dir>~/.nix-profile/share/fonts</dir>
<dir>~/Library/Fonts</dir>
<dir>/usr/share/fonts</dir>
<dir>/usr/local/share/fonts</dir>
<dir>/Library/Fonts</dir>
<dir>/System/Library/Fonts</dir>
<dir>/nix/var/nix/profiles/default/lib/X11/fonts</dir>
<dir>/nix/var/nix/profiles/default/share/fonts</dir>
<dir>/nix/store/qa14zihakvfpx3jmqzhpvn2gygdwdgsw-dejavu-fonts-minimal-2.37</dir>

这些路径就是使用 Nixpkgs 安装的 TeXLive 发行版,XeTeX 进行编译时查找字体会搜索的路径了。

如果是在 Windows 和 GNU/Linux 上,是可以通过设置 FONTCONFIG_FILE 环境变量来指定字体搜索的路径了。但是 Nixpkgs 给程序打了一个 wrapper,不好简单地覆盖了。因此还得搞清楚 CTeX 宏集为什么会找不到正确的字体。

CTeX 字体配置逻辑

查询 CTeX 宏集的源码,发现 macOS 上的字体配置逻辑其实有些问题:

1
2
3
4
% ctex-fontset-mac.def
\file_if_exist:nTF { /System/Library/Fonts/PingFang.ttc }
  { \ctex_file_input:n { ctex-fontset-macnew.def } }
  { \ctex_file_input:n { ctex-fontset-macold.def } }

通过判断字体文件 /System/Library/Fonts/PingFang.ttc 是否存在来决定选取 macnew/macold 预设的方式已经不适用了,因为 macOS 15 把 PingFang.ttc 移到了别的地方。当然,这是 Apple 故意把字体文件藏起来导致的问题,Github 上也有相关的讨论:ctex的Lualatex or/+ MacOS15兼容性问题 · Issue #722 · CTeX-org/ctex-kit

即使手动指定了 fontset=macnew,也可能找不到字体文件,因为楷体、黑体这些常见字体的字体文件也被 Apple 藏了起来,/System/Library/Fonts 下无法找到。

解决方法

先看看 fontset=macnew 会使用哪些字体:

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
% ctex-fontset-macnew.def
\ctex_fontset_case:nnnn
  { \ctex_fontset_error:n { mac } }
  {
    % ...
  }
  {
    % ...
  }
  {
    \setCJKmainfont { Songti~SC~Light }
      [
        BoldFont       = Songti~SC~Bold,
        ItalicFont     = Kaiti~SC,
        BoldItalicFont = Kaiti~SC~Bold
      ]
    \setCJKsansfont { PingFang~SC }
    \setCJKmonofont { STFangsong  }
    \setCJKfamilyfont { zhsong } { Songti~SC~Light } [ BoldFont = Songti~SC~Bold ]
    \setCJKfamilyfont { zhhei  } { Heiti~SC~Light  } [ BoldFont = Heiti~SC~Medium ]
    \setCJKfamilyfont { zhpf   } { PingFang~SC     }
    \setCJKfamilyfont { zhfs   } { STFangsong      }
    \setCJKfamilyfont { zhkai  } { Kaiti~SC        } [ BoldFont = Kaiti~SC~Bold ]
    \setCJKfamilyfont { zhli   } { Baoli~SC        }
    \setCJKfamilyfont { zhyou  } { Yuanti~SC~Light } [ BoldFont = Yuanti~SC~Regular ]
  }

这里只有第四个参数是我们应该在乎的,它设置的是使用 XeTeX/LuaTeX 时的字体。为了保证 TeX 能直接找到这些字体,我将 Apple 藏在深层系统目录下的一些字体文件复制到 ~/Library/Fonts 目录下。目前观测到可能以下位置会放置系统字体文件:

1
2
/System/Library/PrivateFrameworks/FontServices.framework/Resources/Fonts
/System/Library/AssetsV2/com_apple_MobileAsset_Font7

使用 find 命令在这些目录中找到字体文件,然后复制到 ~/Library/Fonts

1
2
3
4
5
$ ls -1 ~/Library/Fonts
Kaiti.ttc
PingFang.ttc
STHEITI.ttf
STXIHEI.ttf

以及在使用 CTeX 的时候,加入选项 fontset=macnew。终于,XeTeX 找到了这些字体,成功编译了我的文档!

其实就是确保 CTeX 需要的这些字体在 fontconfig 文件中的那些路径中能找到。我选择把一开始没找到的字体放在 ~/Library/Fonts 目录下,因为不需要系统权限。当然,放回到 /System/Library/Fonts 目录下也是可行的,而且如果将 PingFang.ttc 放回去,就不需要显式设置 fontset=macnew 了。

本文由作者按照 CC BY 4.0 进行授权