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

彻底解决JavaFx在Linux中文无法正常显示的问题(究其根本原因)

  JavaFX 使用 JPackage 打包后,在 Linux 中运行时简体中文将无法正常显示,你看见的全是方框。网上有各种各样的解决办法,可以说都有用,但终究没有找到根本原因。

网上查了一下,有大概几种解决办法:
1 Linux 缺中文字体(较新的发行版都有中文支持了,确实没有需要安装);
2 JavaFX 程序中代码加载字体;
3 JavaFx 程序中代码设置字体;
4 JavaFx 程序中样式表设置字体;
5 配置JAVA_HOME/jre/lib/fonts/fontconfig.properties(JavaFX打包后没有这个了);
6 复制Windows的字体到Linux系统(吐血)。

要解决问题就要找到根本原因

  搜罗大量国内外文章之后,我们发现了端倪,首先有一篇比较全面的文章 OpenJFX/Font+Setup 可以指导我们排查问题,过程就不描述了,结论就是:JavaFX 默认字体名称(CSS -fx-font-family:)为 "System Regular" 字体族 "System",通过Linux系统命令 “$ fc-match sans.regular” 可输出其映射的实际字体文件 NotoSansCJK-Regular.ttc: "Noto Sans CJK SC" "Regular",其中的SC表示简体中文(Simplified Chinese),如此说来,更本不是 Linux 系统缺简体中文字体(哪个Linux系统没有中文桌面呢,那不是也正确显示中文了吗),其实是 JavaFX 自身的问题。

  网上有一篇文章跟踪了JavaFx的字体加载过程,解释了这个问题的原因,很遗憾我们丢失了链接(这里应该链接到那一篇非常不错的文章);根本原因就是 JavaFx 在 Windows 、MacOS 和 Linux 分别采用了不同的默认字体加载机制,而 Linux 中的加载机制,恰好没有将 “System” 默认为支持中文的字体。

  问题的根源已经找到,接下来想办法解决。

解决JavaFx在Linux中文显示问题

我必须要吐槽,解决这个问题的过程,真的非常痛苦。

1.通过 JavaFX 的样式表像前端那样配置多个字体名称来兼容多个平台(不可行)。

.root {/* JavaFx 中这不会生效 */-fx-font-family: 'Noto Sans CJK SC','Source Han Sans CN' ...;
}

  JavaFx 明确不支持这种配置方式,我们只能指定一个字体名称;指定多个是错误的语法,等同于无效的样式项。

2.通过 @font-face 指定多个本地名称来兼容多个平台(不可行)。

@font-face {/* JavaFx 中此方法不可行 */-fx-font-family: 'FontDown';src: local('Noto Sans CJK SC'),local('PingFang SC'), local('Hiragino Sans GB'),local('Source Han Sans CN'),              local('WenQuanYi Micro Hei'),url("AlibabaPuHuiTi-3-45-Light.ttf");
}
.root {-fx-font-family: 'FontDown';
}

  这个思路是不是完美无缺,但是实际上 JavaFx 的 CSS 文档中明确告知,这也不支持。

Although the parser will parse the syntax, all @font‑face descriptors are ignored except for the src descriptor. The src descriptor is expected to be a <url>. The format hint is ignored.

  翻译过来就是: 尽管解析器会解析该语法,但除 src 描述符外,所有 @font-face 描述符均会被忽略。src 描述符应为 <url> 类型,而 format 格式提示将被忽略。

   也就是说 @font-face 中只有 src:url() 被支持,其余的你爱咋写咋写,我全忽略。以上引用自JavaFX CSS 参考指南 。

3.通过@font-face加载指定字体文件并作为指定显示字体(可行)

  但这里有个大坑我们要注意:在 JavaFx 的 @font-face 中 font-family:'name' 也无效,因此只能使用字体文件默认的 font-family 名称,听起来也可以接受,那就用默认的名称把;

  用默认的名称也有个大坑,如果你用的是具有中文名称的字体,例如:阿里巴巴普惠体;我们还要吐血一次,在 JavaFx 中字体的 font-family 名称随操作系统而不同,我们以阿里巴巴普惠体为例,详细说明。

  如果在Windows系统中安装阿里巴巴普惠体,在JavaFx中可以用以下方式指定字体,测试有效。

-fx-font-family: '阿里巴巴普惠体 3.0 45 Light';

  如果在 JavaFx 的 @font-face 中加载阿里巴巴普惠体,使用上面的字体名称无效,实际的字体名称变成了另外的名称,有效名称如下所示。

-fx-font-family: 'Alibaba PuHuiTi 3.0 45 Light';

  我们是通过以下代码,找出加载的字体在 JavaFx 中是以什么名称映射的。

final Font[] fonts = Font.loadFonts(getClass().getResourceAsStream("AlibabaPuHuiTi-3-45-Light.ttf"), 13);
for (Font font : fonts) {System.out.println(font.getFamily());
}

  总结一下:JavaFX 通过 CSS @font-face 加载的字体,font-family 名称可能不是我们预期的,并且也无法指定名称。

最终解决办法

1. 如果我们是特定的 Linux 系统可以直接通过JavaFX 的 CSS指定操作系统默认支持中文的字体,如果指定的字体不存在,将自动回退为 "System Regular",这将导致中文显示为方框,我们得提醒用户安装指定的字体。

.root {-fx-font-family: 'Noto Sans CJK SC';	
}

2.通过程序判断有哪些支持的中文字体,并设置到 JavaFx 窗口加载的根 Node ,此方法的好处是可以自动适配多个 Linux 平台以及其它平台,但是麻烦的是,我们需要为每个加载的窗口,添加代码;JavaFx 没有全局窗口的样式配置方式。

	public static void defaultFont(Parent parent) {final List<String> families = Font.getFamilies();if (families.contains("Noto Sans CJK SC")) {parent.setStyle("-fx-font-family: 'Noto Sans CJK SC';");} elseif (families.contains("Source Han Sans CN")) {parent.setStyle("-fx-font-family: 'Source Han Sans CN';");} elseif (families.contains("WenQuanYi Micro Hei")) {parent.setStyle("-fx-font-family: 'WenQuanYi Micro Hei';");} elseif (families.contains("Hiragino Sans GB")) {parent.setStyle("-fx-font-family: 'Hiragino Sans GB';");} elseif (families.contains("PingFang SC")) {parent.setStyle("-fx-font-family: 'PingFang SC';");}}

  至此 JavaFx 中文字体问题,彻底解决;期待 JavaFx 在后续的版本中修复此问题,我们当前测试的环境是 Debian 12 + JavaFx 17。

  以上是我们在开发 r11 程序时,为了通过 jpackage 将 javaFx 桌面程序打包为最终镜像时遇到的问题;r11 是一款用于将软件源代码导出为软件著作权代码文档的工具,推荐去官网 r11.joyzl.cn 下载试试。

http://www.lryc.cn/news/584843.html

相关文章:

  • 飞书CEO谢欣:挑战巨头,打造AI新时代的Office
  • 锁的艺术:从Mutex到ReentrantLock,掌握并发编程的脉搏
  • 分布式系统高可用性设计-负载均衡与容错机制深度解析
  • Shader面试题100道之(81-100)
  • 模拟实现unordered_map
  • 如何使用 Python 删除 Excel 中的行、列和单元格 – 详解
  • 如何从0开始构建自己的第一个AI应用?(Prompt工程、Agent自定义、Tuning)
  • 格密码--数学基础--02基变换、幺模矩阵与 Hermite 标准形
  • AI金融风控:识别欺诈,量化风险的新利器
  • pandas销售数据分析
  • python 在 Linux CentOS 上安装 playwright 的完整步骤
  • Pandas:常见的转换函数(rename,set_index,reset_index)
  • MD2Doc转换器(基于Python)
  • [Reverse1] Tales of the Arrow
  • 飞算 JavaAI 深度体验:开启 Java 开发智能化新纪元
  • 闲庭信步使用图像验证平台加速FPGA的开发:第八课——图像数据的行缓存
  • Locust 负载测试工具使用教程
  • 为什么选择Selenium自动化测试?
  • [特殊字符]远程服务器配置pytorch环境
  • ajax和XMLHttpRequest以及fetch
  • STM32-DAC数模转换
  • day21——特殊文件:XML、Properties、以及日志框架
  • C#元组:从基础到实战的全方位解析
  • 实现在线预览pdf功能,后台下载PDF
  • 使用gdal读取shp及filegdb文件
  • 通过ETL工具,高效完成达梦数据库数据同步至数仓Oracle的具体实现
  • Primer Premier 5分子生物学引物设计软件 PCR引物设计工具
  • Swift 解 LeetCode 324:一步步实现摆动排序 II,掌握数组重排的节奏感
  • 智能文本抽取在合同管理实战应用
  • P1484 种树,特殊情形下的 WQS 二分转化。