LaTeX中文字体缺失报错排查:FangSong_GB2312找不到的完整解决方案
2026/9/9 9:31:09 网站建设 项目流程

这两天在帮一个项目组调LaTeX编译流水线,结果一跑到文档编译那步就挂,日志里明晃晃一行:

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ! ! fontspec error: "font-not-found" ! ! The font "FangSong_GB2312" cannot be found. ! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

这个报错在texlive2025环境里太典型了,尤其是在Linux服务器、WSL或者Docker容器里编译带中文的文档时。Windows上跑得好好的项目,一挪到Linux就炸,十有八九都是字体问题。我这几年踩过的坑,从挖fontconfig配置到手动改字体名,基本都集中在这条报错上。这篇文章就完整梳理一下:FangSong_GB2312到底是什么,为什么texlive找不到它,以及在不同场景下怎么彻底解决。

不管你是刚接触LaTeX的新手,还是被CI构建折磨的运维,又或者是写毕业论文被模板坑过的人,这篇都能给你一套能直接抄的解决方案。

1. 先别急着改代码:弄懂FangSong_GB2312到底去哪了

1.1 公文排版和论文模板为什么非要FangSong_GB2312

先说字体本身。仿宋_GB2312是汉字排版里的老熟人,国内党政机关公文格式要求正文用仿宋_GB2312,高校毕业论文模板里也大量出现这个字体名。它的罗马化字体族名通常写作FangSong_GB2312,在Windows系统里经常以“仿宋_GB2312”这个名字出现在字体列表中。

这里有个特别容易搞混的点:Windows系统自带的“仿宋”字体,字体族名是FangSong,文件名是simfang.ttf,这个是系统自带的;而“仿宋_GB2312”字体族名是FangSong_GB2312,它往往不是Win系统默认自带的,很多是跟着Office套件或者用户自己安装进去的。这俩是两套不同的字体,字形细节和字库范围都有差异。很多人在Linux上装了个simfang.ttf,以为问题解决了,结果LaTeX还是报cannot find FangSong_GB2312,就是因为实际装错了字体。

在LaTeX文档里,这个字体名出现的典型方式有两种:

% 方式一:ctex宏包配合fontset=windows时,部分模板会显式覆盖字体 \documentclass[fontset=windows]{ctexart} \setCJKmainfont{FangSong_GB2312} % 方式二:直接用fontspec/xeCJK声明 \usepackage{xeCJK} \setCJKmainfont{FangSong_GB2312}

问题就出在这:当你用xelatex或lualatex编译时,引擎不会自己去扫描字体文件,而是通过系统的字体服务(Linux/macOS上是fontconfig,Windows上是DirectWrite)来查找字体。查不到这个字体名,就直接抛异常退出,报错信息就是cannot find FangSong_GB2312。

1.2 为什么Windows编译正常、Linux/WSL上就报错

核心原因很简单:字体不是跨平台的。Windows字体是Windows的,Linux系统默认不会给你装任何Windows专有字体。texlive2025自带的中文字体是Fandol系列(FandolSong、FandolHei、FandolKai、FandolFang)和Arphic系列,这些是开源字体,覆盖了宋、黑、楷、仿四类基本书体,但字体名里没有FangSong_GB2312。

所以只要文档里写了\setCJKmainfont{FangSong_GB2312},编译时fontconfig就会去字体库里找这个名字。找不到,报错,结束。这个过程不区分你用的是texlive2023还是texlive2025,也不管你的LaTeX宏包是不是最新版——字体查找是引擎层面的行为,跟LaTeX版本关系不大。

我把常见平台的默认情况整理成了一张表:

平台是否自带FangSong_GB2312常见自带仿宋字体说明
Windows(本机已安装字体)部分有FangSong(仿宋)、仿宋_GB2312能否编译取决于到底装没装过“仿宋_GB2312”
Windows(纯净系统)不一定FangSong(仿宋)如果模板里写的是FangSong_GB2312,可能也会报错
Linux(Ubuntu/Debian/CentOS)texlive自带FandolFang,但名字不同
WSL(默认配置)WSL默认不加载Windows字体,需要额外配置
macOSSTFangsong(华文仿宋)字体名完全不同,需要映射或改名
Docker容器需要额外安装字体包或COPY字体镜像

这也是为什么同一个项目在不同人电脑上编译结果天差地别的原因。

1.3 ctex的fontset机制:自动选择不等于万事大吉

ctex宏包提供了一个fontset参数,可以指定从哪套字体配置里取字。比如:

\documentclass[fontset=windows]{ctexart} % 使用Windows字体 \documentclass[fontset=fandol]{ctexart} % 使用Fandol开源字体 \documentclass[fontset=mac]{ctexart} % 使用macOS华文字体

如果不显式指定,ctex 2.x会根据当前操作系统自动推断:在Windows上用SimSun那一套,在macOS上找华文字体,在Linux上用Fandol。理论上这样设计很智能,但问题在于很多老模板里会同时手动覆盖\setCJKmainfont,把字体写死成FangSong_GB2312。一旦模板里写了这种硬编码,ctex的fontset再怎么自动选择也白搭——你手动声明的字体会覆盖掉ctex的默认配置。

所以排查这个报错的时候,第一件事不是急着装字体,而是先搞清楚:这个字体名到底是在哪里被声明的。是文档导言区显式写的,还是模板.cls文件里写的,又或者是从ctex的某个fontset配置里带出来的。定位源头的思路不同,后续处理方式完全不同。

2. 排查不是瞎猜:三个命令定位问题

2.1 先确认你的编译引擎和字体配置来源

在动手修之前,先把问题范围缩小。我的习惯是分三步走:

  1. 确认编译引擎:打开项目,看是用xelatex还是lualatex编译。字体查找机制的入口不同,但最终都落在fontconfig上。用latexmk的话,可以看下latexmkrc文件里配置的是xelatex还是lualatex。

  2. 打开文档源码或模板.cls文件,全局搜索一下“FangSong_GB2312”这个字符串,看它出现在哪里。这一步能直接告诉你问题源头。

  3. 把编译命令完整跑一遍,确保报错确实是字体错误而不是其他宏包错误。有时候前提搞错了,后面全是白费功夫。

在我见过的项目里,90%的情况是模板.cls文件里硬编码了\setCJKmainfont{FangSong_GB2312},剩下10%是用户自己写的导言区。

2.2 用fc-list确认系统里到底有没有这个字体

在Linux或WSL终端里跑这个命令:

fc-list :lang=zh

这条命令会列出系统里所有支持中文的字体。输出里会包含字体文件路径、字体族名(中英文)、样式等信息。如果系统里装了仿宋相关的字体,你多半能在输出里看到类似这样的行:

/usr/share/fonts/truetype/fangsong/simfang.ttf: 仿宋,FangSong:style=Regular

注意看冒号后面的是字体族名。如果有FangSong但没FangSong_GB2312,那说明你(或系统)安装的是“仿宋”而不是“仿宋_GB2312”。如果连仿宋的影子都没有,那就更直接了——系统里根本没有对应的字体。

再精确验证一下:

fc-match "FangSong_GB2312"

如果fontconfig里能找到这个字体,fc-match会输出字体文件路径,比如:

/usr/share/fonts/truetype/custom/FangSong_GB2312.ttf: 仿宋_GB2312,FangSong_GB2312:style=Regular

如果找不到,fc-match会输出一个空行或一个替代字体的路径(fallback结果)。这也能侧面说明,LaTeX报cannot find FangSong_GB2312是完全正常的——系统层面就不存在这个字体。

2.3 字体存在但LaTeX还是找不到?排查字体名的不一致

还有一种情况比较隐蔽:字体文件装好了,fc-list也能看到了,但LaTeX编译还是报找不到。这多半是字体名不一致的问题。

Linux上安装的字体,如果字体文件内部的family name是中文字段“仿宋_GB2312”,那么fc-list里显示的主名可能是中文“仿宋_GB2312”,而罗马化的“FangSong_GB2312”可能只作为别名出现,甚至完全没有。xelatex在匹配字体名时,对中文名和英文名的处理策略不完全一样,有时候你写中文名能匹配上,写英文名就匹配不上。

判断方法是用fc-scan查看字体文件的详细信息:

fc-scan /usr/share/fonts/truetype/custom/FangSong_GB2312.ttf

输出里会有family和fullname字段,能直观看到字体的真实名字。如果family是“仿宋_GB2312”而没有包含FangSong_GB2312这个别名,那你在文档里写\setCJKmainfont{FangSong_GB2312}就可能认不出来,这时需要改成\setCJKmainfont{仿宋_GB2312}(中文名)来试。

这一步排查很重要,很多人栽在这个细节上,以为装好了字体就应该万事大吉,结果卡在名字匹配上几个小时。

3. 四种解决方案:从改系统到改模板

3.1 方案一:把字体装进系统(治本)

如果项目必须用FangSong_GB2312这个字体,且你不能改模板,那最可靠的做法就是把字体装进系统里。

字体来源:从有FangSong_GB2312的Windows机器上,将字体文件复制出来。注意版权问题,这种字体的使用许可通常不允许随意分发,但个人项目、公司内网使用一般问题不大,不能用于商业再分发。

拿到字体文件后,在Linux上安装到用户目录即可,不需要sudo:

mkdir -p ~/.fonts cp FangSong_GB2312.ttf ~/.fonts/ fc-cache -fv ~/.fonts

如果想装到系统全局,给所有用户使用:

sudo mkdir -p /usr/share/fonts/truetype/custom sudo cp FangSong_GB2312.ttf /usr/share/fonts/truetype/custom/ sudo fc-cache -fv

装完验证:

fc-list | grep -i fangsong

看到FangSong_GB2312出现在输出里,再重新编译LaTeX文档,应该就不会报这个错了。

这里有个重要提醒:如果你复制的是Windows自带的simfang.ttf,那只是“仿宋”(FangSong),不是“仿宋_GB2312”。必须确认字体文件对应的family name真的是FangSong_GB2312,用fc-scan看清,否则装完还是会报错。

3.2 方案二:改用texlive自带的Fandol字体(省事)

如果不强制要求“仿宋_GB2312”这个字体,只是需要一份仿宋风格的中文正文,那最省事的办法是改用Fandol字体。texlive2025本身就带了Fandol系列,无需额外安装任何字体。

改法很简单,把文档导言区的字体声明改成:

\documentclass[fontset=fandol]{ctexart}

如果之前没有用ctex文档类,而是手动写的字体配置,那把\setCJKmainfont的参数改掉:

\usepackage{xeCJK} \setCJKmainfont{FandolFang}

FandolFang是Fandol系列的仿宋体,字形规整,排版效果和仿宋_GB2312其实很接近。至少从“正文是仿宋风格”这个需求上讲,FandolFang完全够用。

这个方案的优点是零依赖、部署简单,CI环境里不用有额外字体也能编译。缺点也很明显:如果是一个严格要求“仿宋_GB2312”字形的公文或学校模板,FandolFang的笔画细节和GB2312标准字库还是有差异的,在一些笔画结构上能看出区别。如果你只是想让编译通过,不在乎字体严格一致,这个方案性价比最高。

3.3 方案三:把字体文件放进项目目录(迁移友好)

如果既不想改模板,又不想动系统字体,还有一个折中方案:把字体文件放在项目目录下,通过fontspec的Path参数指定字体路径。

项目结构示例:

project/ ├── main.tex ├── fonts/ │ └── FangSong_GB2312.ttf └── compile.sh

文档里这样写:

\usepackage{xeCJK} \setCJKmainfont{FangSong_GB2312}[ Path = ./fonts/ , Extension = .ttf , UprightFont = FangSong_GB2312 , BoldFont = FangSong_GB2312 ]

这样编译时,xelatex会直接去指定目录找字体文件,而不依赖系统字体库。好处是项目自包含,换机器clone后直接编译就能过,不需要额外装字体。坏处是如果文档里很多地方用到这个字体,每处都要写Path参数,比较啰嗦;而且改路径时文件层级变了容易出错。

另外一个变体:也可以不指定Path,而是把字体文件放到texlive的字体搜索路径里,比如texmf-local目录。但这样做维护成本高,不如直接在项目里指定路径来得直观。

3.4 方案四:WSL里让fontconfig直接识别Windows字体

如果你用的是WSL(Windows Subsystem for Linux),还有一个独特的手段:让WSL里的fontconfig直接读取Windows字体目录。Windows字体在WSL里通常挂载在/mnt/c/Windows/Fonts。

创建或编辑fontconfig配置文件:

mkdir -p ~/.config/fontconfig cat > ~/.config/fontconfig/fonts.conf << 'EOF' <?xml version="1.0"?> <!DOCTYPE fontconfig SYSTEM "fonts.dtd"> <fontconfig> <dir>/mnt/c/Windows/Fonts</dir> </fontconfig> EOF

然后刷新缓存:

fc-cache -fv

执行完后再用fc-list验证:

fc-list | grep -i fangsong

如果Windows系统里装了仿宋_GB2312字体,这时WSL里就能直接看到它了。这个方案的优点是完全不用复制字体文件,WSL和Windows共享字体;缺点是走/mnt/c的I/O效率较低,大量字体加载时会稍慢,另外如果后续把编译环境从WSL迁到纯Linux服务器,这套配置就失效了。

我个人的使用建议:WSL里调试阶段可以用这个方案快速验证,但最终要上CI/服务器时,还是走方案一或方案三更稳。

4. 实操记录:一次完整的Linux服务器LaTeX编译修复

4.1 报错现场还原

事情是这样的:一个论文模板项目,在Windows本机用TeXstudio编译一切正常。代码推到GitLab后,CI用流水线执行编译,结果在那台Ubuntu 22.04的构建机上直接挂了。日志片段长这样:

Executing command: xelatex -interaction=nonstopmode -halt-on-error main.tex ... Package fontspec Error: The font "FangSong_GB2312" cannot be found.

项目里用的是ctexart文档类,导言区长这样:

\documentclass[fontset=windows]{ctexart} \setCJKmainfont{FangSong_GB2312} \setCJKsansfont{SimHei} \setCJKmonofont{KaiTi}

问题很清楚:fontset=windows要求系统里有Windows字体,但构建机是干净的Ubuntu,什么都没装。而且模板里硬编码了FangSong_GB2312,所以ctex的自动回退机制也失效了。

4.2 修复步骤

我用的是“模板改Fandol + 项目内字体文件兜底”的双保险策略,这样既解决当前编译,又避免以后换机器还踩同样的坑。

第一步,先在服务器上确认texlive2025已装好,xelatex可用:

which xelatex xelatex --version | head -n 1

第二步,检查系统里有没有中文字体:

fc-list :lang=zh

输出是空的,果然一个中文字体都没装。这时候如果直接用Fandol,理论上不需要额外装字体——texlive自带。但保险起见,我还是安排了一个字体文件放入项目目录,方便将来有需求时直接调用。

第三步,修改模板文件的字体配置。把导言区改为:

\documentclass[fontset=fandol]{ctexart}

同时把所有\setCJKmainfont{FangSong_GB2312}这样显式指定字体的行注释掉或改成:

%\setCJKmainfont{FangSong_GB2312} \setCJKmainfont{FandolFang}

并把\setCJKsansfont{SimHei}改成FandolHei,\setCJKmonofont{KaiTi}改成FandolKai。

第四步,重新编译:

latexmk -xelatex -halt-on-error main.tex

编译通过,PDF正常生成,内容和Windows上编译的版式几乎无差别。

4.3 如果要强行保留FangSong_GB2312呢

如果模板不能改,那就在构建机上装字体。我在另一台测试机上试过完整流程:

sudo mkdir -p /usr/share/fonts/truetype/custom sudo cp FangSong_GB2312.ttf /usr/share/fonts/truetype/custom/ sudo fc-cache -fv fc-match "FangSong_GB2312"

输出显示匹配到了字体文件后,直接latexmk编译,同样通过,不再报cannot find。

这里我的建议是:如果项目逻辑允许改模板,优先用Fandol方案,因为部署成本为零,还不用处理字体版权分发问题。如果必须用原字体,那就老老实实在服务器上装字体,并且把字体文件放到CI的构建配置里一起管理,不要指望每次构建时手工操作。

5. 你可能还会遇到的五个坑

5.1 字体装好了但LaTeX还是报找不到

如果你用的是lualatex编译,它有自己的字体缓存(luaotfload),跟fontconfig是两层。装了新字体后,lualatex不一定能立刻识别。

解决办法是清缓存重建:

luaotfload-tool --clear-cache luaotfload-tool --update-cache

清完再编译,通常就能找到了。这个问题在xelatex里基本不会遇到,xelatex走的是fontconfig,实时读取。

5.2 字体名对不上:中文名vs英文名

再强调一次,Linux上安装字体后,字体在fc-list里显示的名字可能和Windows注册名不一样。上面提到过,用fc-scan查看实际名称:

fc-scan FangSong_GB2312.ttf | grep -E "family|fullname"

如果family是“仿宋_GB2312”,没有FangSong_GB2312别名,那在文档里可以这么写:

\setCJKmainfont{仿宋_GB2312}

不过这个写法在不同系统和字体配置下兼容性略差,能想办法补充字体别名最好。比较稳妥的做法是用FontForge等工具修改字体文件的family name,加一个FangSong_GB2312的别名。但这样做会改变字体文件指纹,一般人不建议折腾。我的经验就是:先试文档里写中文名,能跑通就不用折腾;跑不通再考虑改字体元数据。

5.3 模板里除了仿宋_GB2312还有楷体_GB2312等一整套GB字体

这类模板一般不止用仿宋_GB2312一种字体,往往还有黑体、楷体、宋体的一整套GB2312系列。在Linux上想全套装齐,最好一次性处理掉。常见需要的有:

字体名用途对应Linux替代
仿宋_GB2312正文FandolFang
楷体_GB2312二级标题、引文FandolKai
黑体(SimHei)一级标题FandolHei
宋体(SimSun)部分模板正文FandolSong

如果不想逐个复制Windows字体,也可以直接考虑安装fonts-noto-cjk包,然后手动把字体映射到GB2312字体名上,但字形会不是仿宋风格,一般不建议这么干。

5.4 CI环境没有sudo权限怎么办

很多CI Runner不会给你sudo。这时不能用/usr/share/fonts目录,改为装到用户级目录:

mkdir -p ~/.fonts cp FangSong_GB2312.ttf ~/.fonts/ fc-cache -fv ~/.fonts

如果连~/.fonts方案也不行(比如Runner是容器,每次构建环境都是全新的),那就只能走方案三——把字体文件放进项目,用Path指定。这是最保险的做法,不依赖任何环境预装。

5.5 Docker镜像里字体丢失问题

Docker镜像默认只有极简字体,连中文字体包都没有。建议在Dockerfile里直接安装字体包:

RUN apt-get update && apt-get install -y fonts-noto-cjk \ && rm -rf /var/lib/apt/lists/*

如果必须用FangSong_GB2312,就把字体COPY进镜像:

COPY FangSong_GB2312.ttf /usr/share/fonts/truetype/custom/ RUN fc-cache -fv

还有一点:Docker构建时如果用了多阶段构建,要注意最终运行阶段里有没有把字体文件COPY过去。经常有人第一阶段装好了字体,第二阶段换了基础镜像,结果字体全没了,编译照样失败。

6. 写到最后的一点个人体会

搞定了好几个项目的这个问题之后,我最大的感受是:字体问题看似简单,背后的机制却串联了操作系统字体管理、文本渲染引擎、LaTeX字体配置三个层面。真正上手时,不要一开始就想着“把字体装上”,而是先弄清楚系统层面有没有这个字体、引擎是怎么找字体的、文档里是怎么声明的。把这三件事对齐了,很多匪夷所思的报错会瞬间变得简单。

另外,如果是长期维护的项目,我强烈建议把字体方案固化到项目配置里——要么明确用Fandol,要么把字体文件和路径配置提交到仓库。千万不要依赖“我这台机器上有这个字体”这种隐式前提,换环境时一定会出问题。CI环境里尤其如此,能在构建配置里显式写明依赖的字体,就能省下一堆半夜排查构建失败的痛苦。

最后再分享一个小技巧:如果你临时要在一台机器上快速判断问题是不是字体缺失,其实不用一行行翻LaTeX日志。先fc-match那个报错里的字体名,如果输出为空,直接去看文档里是不是显式声明了这个字体——两个问题一确认,根因就锁定了。剩下的事情,就是选一种适合你场景的方案执行而已。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询