当前位置: 首页 > news >正文

Java 加载自定义字体失败?从系统 fontconfig 到 Maven 损坏的全链路排查指南

📌 背景
SpringBoot3.5.3
jdk17

Font.createFont() 调用,报错:

java.io.IOException: Problem reading font data
java.lang.NullPointerException: Cannot load from short array because "sun.awt.FontConfiguration.head" is null

更诡异的是:同样的代码、同样的 JAR 包,在测试环境正常,生产环境却失败!

本文从 系统层、构建层、代码层 三方面,完整复盘一次真实生产事故的排查过程,最终定位到 fontconfig 缺失 + Maven 资源损坏 的双重问题,并提供可落地的解决方案。

🧩 问题现象
使用 Font.createFont(Font.TRUETYPE_FONT, inputStream) 加载 msyhl.ttc(微软雅黑轻型)
测试环境正常,正式环境报错:Problem reading font data
JAR 包相同,JDK 版本一致(OpenJDK 17)
字体文件存在于 resources/fonts/,且能通过 getResourceAsStream 读取(is != null)
🔍 排查思路:从底层到上层
我们采用 “自底向上” 的排查方法,优先检查系统依赖,再看构建过程,最后分析代码逻辑。

第一步:检查系统级字体支持 —— fontconfig 是否安装?
这是最容易被忽略,但最关键的一环!

❓ 为什么 fontconfig 如此重要?
Java(尤其是 OpenJDK)在 Linux 环境下依赖系统级的字体管理库:

fontconfig:负责字体发现、匹配、缓存
freetype:负责字体解析和渲染
如果新服务器是 最小化安装(如 CentOS minimal、Docker 镜像),很可能 默认未安装 fontconfig,导致 JVM 无法正常解析 .ttc 等复杂字体。

✅ 验证命令:

# 检查是否安装
rpm -q fontconfig || echo "❌ fontconfig 未安装"
rpm -q freetype || echo "❌ freetype 未安装"
# 查看字体缓存
fc-list | grep -i "yahei\|sim"

🛠 解决方案:

# 安装字体支持库
sudo yum install -y fontconfig freetype
# 更新字体缓存(关键!)
sudo fc-cache -fv

✅ 安装后无需重启 JVM,Java 程序即可正常加载字体。

第二步:检查 Maven 构建过程 —— 字体文件是否被“损坏”?
即使系统 fontconfig 正常,Maven 构建过程也可能破坏字体文件。

❓ 为什么 Maven 会“损坏”字体?
Maven 默认以 文本模式 处理资源文件,可能对 .ttc、.ttf 等二进制文件进行:

换行符转换(\n → \r\n)
字符编码转换
${} 占位符替换
这些操作会 破坏字体的二进制结构,导致 createFont() 失败。

✅ 验证方法:
提取 JAR 中的字体文件,检查大小:

jar -xf your-app.jar BOOT-INF/classes/fonts/msyhl.ttc
ls -l BOOT-INF/classes/fonts/msyhl.ttc

✅ 正常大小:约 9.3MB(9844184 字节)
❌ 异常大小:几十 KB 或几百 KB → 被 Maven 损坏
🛠 解决方案:在 pom.xml 中使用 保护字体文件不被 Maven 损坏

<build><resources><!-- 资源1: 所有资源默认参与过滤,但排除 fonts/ 和特定配置文件 --><resource><directory>src/main/resources</directory><filtering>true</filtering><excludes><!-- 排除字体文件:防止被文本过滤破坏二进制结构 --><exclude>fonts/*</exclude><!-- 排除其他不希望被过滤的二进制文件 --><exclude>*.bin</exclude><exclude>*.dat</exclude><!-- 排除多环境配置文件(由下一个 resource 处理) --><exclude>application-dev.yml</exclude><exclude>application-test.yml</exclude><exclude>application-pro.yml</exclude></excludes></resource><!-- 资源2: 字体文件单独处理,关闭 filtering --><resource><directory>src/main/resources</directory><filtering>false</filtering><includes><include>fonts/*</include></includes></resource><!-- 资源3: 动态加载指定环境的配置文件 --><resource><directory>src/main/resources</directory><filtering>true</filtering><includes><!-- 根据 -DprofileActive=dev 动态包含对应配置 --><include>application-${profileActive}.yml</include></includes></resource></resources><plugins><!-- 其他插件配置 --></plugins>
</build>

✅ 这样 Maven 就不会对字体文件做任何文本处理,确保二进制完整性。

第三步:检查 Java 代码 —— InputStream 是否被正确使用?
即使前两步都正确,代码层面也可能出问题。

❌ 常见错误写法:

InputStream is = getClass().getResourceAsStream("/fonts/msyhl.ttc");
Font font = Font.createFont(Font.TRUETYPE_FONT, is); // ❌ 流可能被提前关闭
Font.createFont() 内部可能异步读取流,而 try-with-resources 会提前关闭流,导致读取不完整。

✅ 正确做法:先读入内存

public static Font loadFont(String fontFileName, int fontType, int fontSize) {try (InputStream is = CarfiCommonUtils.class.getResourceAsStream("/fonts/" + fontFileName)) {if (is != null) {byte[] fontBytes = is.readAllBytes(); // 读入内存try (InputStream bis = new ByteArrayInputStream(fontBytes)) {return Font.createFont(Font.TRUETYPE_FONT, bis).deriveFont(fontType, fontSize);}}} catch (Exception e) {log.error("加载字体失败", e);}return new Font("Arial", fontType, fontSize); // 兜底字体
}

✅ 最佳实践总结

层级措施说明
系统层yum install fontconfig freetype确保 OpenJDK 字体子系统正常
构建层使用 resources保护字体文件防止 Maven 损坏二进制资源
代码层readAllBytes() + 内存流避免流关闭问题
字体格式优先使用 .ttf 而非 .ttc.ttc 复杂,兼容性差
兜底策略提供默认字体(如 Arial)防止因字体失败导致服务不可用

层级 措施 说明

🧪 推荐:生产环境字体测试工具
编写一个简单的 FontTest.java,用于验证字体是否可加载:

public class FontTest {public static void main(String[] args) {try (InputStream is = FontTest.class.getResourceAsStream("/fonts/msyhl.ttc")) {if (is == null) {System.err.println("❌ 字体文件未找到");return;}byte[] data = is.readAllBytes();System.out.println("📊 字体大小: " + data.length + " bytes");Font font = Font.createFont(Font.TRUETYPE_FONT, new ByteArrayInputStream(data));System.out.println("✅ 字体加载成功: " + font.getFontName());} catch (Exception e) {e.printStackTrace();}}
}

部署前在目标服务器运行此脚本,提前发现问题。

📣 结语
Java 字体加载失败,看似是代码问题,实则可能是 系统依赖缺失 + 构建过程污染 的复合问题。

不要只盯着代码,要从系统、构建、代码三层联动排查。

希望本文能帮你少走弯路,避免在生产环境“抓瞎”。

如果你也在 Docker 中遇到此问题,记得在 Dockerfile 中安装:

RUN apt-get update && apt-get install -y fontconfig
# 或
RUN yum install -y fontconfig freetype
http://www.dtcms.com/a/361735.html

相关文章:

  • 基于 C 语言的网络单词查询系统设计与实现(客户端 + 服务器端)
  • 适合工程软件使用的python画图插件对比
  • Maven - Nexus搭建maven私有仓库;上传jar包
  • 20250829的学习笔记
  • OPENCV 基于旋转矩阵 旋转Point2f
  • 代码随想录二刷之“回溯”~GO
  • 机器翻译:python库translatepy的详细使用(集成了多种翻译服务)
  • Spring框架入门:从IoC到AOP
  • 爬虫实战练习
  • 如何在Github中创建仓库?如何将本地项目上传到GitHub中?
  • IDEA Spring属性注解依赖注入的警告 Field injection is not recommended 异常解决方案
  • Python绘制多彩多角星实战
  • MyBatis 性能优化最佳实践:从 SQL 到连接池的全面调优指南
  • 链表相关OJ题
  • MongoDB 备份与恢复:mongodump 和 mongorestore 实战
  • NestJS 3 分钟搭好 MySQL + MongoDB,CRUD 复制粘贴直接运行
  • Flutter Container 阴影设置指南 2025版
  • Flutter 完全组件化的项目结构设计实践
  • 复刻elementUI的步骤条Steps
  • 【架构师干货】系统架构设计
  • Pytorch的CUDA版本安装使用教程
  • XGBoost学习笔记
  • docker 数据管理
  • 徐州服务器:机柜租用具体包含哪些内容?
  • 『Java』把Java项目打成Jar包,并引用项目外的Jar依赖
  • Spring Boot 常用注解有哪些?
  • 【MySQL】进阶技术详解
  • 机器学习-时序预测2
  • uniapp使用uview UI,自定义级联选择组件
  • 正则表达式与grep文本过滤详解