Swing文本组件选中时连字与字距调整失效问题
连字(Ligatures)
在Java 17中使用Swing时,遇到了一些奇怪的渲染故障:启用字体连字并切换到连字密集的字体(如Fira Code),选中文本的部分连字内容会出现以下现象:
- 选中的文本似乎脱离上下文重新渲染,但连字外的部分保持不变。
[连字失效示例]:选中的连字文本与未选中部分样式脱节,连字效果异常 - 取消选中后,文本光标(caret)会在字母间留下未渲染的空白。
[取消选连字文本留下未渲染空白]:取消选中操作后,光标位置出现空白区域 - 上述两个问题可能同时出现,导致内容难以辨认。
[两个问题同时出现]:连字失效与空白区域同时存在,文本可读性严重下降
字距调整(Kerning)
使用字距调整密集的字体(如Windows 11新默认字体Segoe UI或Segoe UI Variable)时,会出现以下故障:
- 选中的文本重新渲染时未结合全文本上下文计算字距。
[字距调整文本选中重渲染动画]:选中文本的字距未按全文本上下文计算,样式发生突变 - 在长段字距调整后的文本中放置光标几乎不可能——内部选择逻辑似乎基于未调整字距的版本计算目标位置。
[字距调整文本选中偏移故障动画]:光标定位逻辑依赖未调整字距的文本,无法准确放置光标 - 取消选中时,字距调整后的文本也无法正确重新渲染。
[取消选字距文本留下未渲染空白动画]:取消选中后,文本无法恢复正确的字距渲染,出现空白区域
分析
该现象至少从Java 1.8版本开始出现,未测试更早版本。不同LAF(外观风格)对故障无影响。问题根源疑似为java.desktop/javax.swing.text.PlainView,尤其是其将文本拆分为可分段处理、仅重渲染“损坏”文本(updateDamage#668)及计算变更边界框的方式。
临时解决方案
唯一稳定有效的解决方案是禁用字距调整和连字。尝试自定义PlainView类接入Swing,但其架构不支持此类修复。
问题咨询
为更好理解问题,特提出以下疑问:
- 是否遇到过此类问题?测试示例是否过于特殊?
- 其他渲染管线是否也受影响?仅在Windows 11测试,推测可能受影响。
- 问题源于JVM标准库还是存储的字体指标非标准?
- 是否了解其他可改善情况的临时解决方案?
- 能否替换Swing默认的字体渲染管线?
感谢帮助!以下为测试源码及机器配置:
补充:
该问题在Mac OSX Big Sur和Java 17环境下也存在,使用默认字体Helvetica时会出现连字和字距调整相关渲染异常,这表明问题可能源于JDK。
import javax.swing.*; import java.awt.*; import java.awt.font.*; import java.util.*; /** Tested on: Edition Windows 11 Pro Version 22H2 OS build 22623.875 Experience Windows Feature Experience Pack 1000.22636.1000.0 openjdk 17.0.4.1 2022-08-12 OpenJDK Runtime Environment Temurin-17.0.4.1+1 (build 17.0.4.1+1) OpenJDK 64-Bit Server VM Temurin-17.0.4.1+1 (build 17.0.4.1+1, mixed mode, sharing) **/ public class Main { private static final boolean TEST_KERNING = true; private static final boolean TEST_LIGATURES = true; private static final String TEST_FONT = "Segoe UI Variable"; private static final Number TEST_FONT_SIZE = 40; private static final Number TEST_FONT_WIDTH = TextAttribute.WEIGHT_BOLD; public static void main(String[] args) throws Exception { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); Map<TextAttribute, Object> textAttributes = new HashMap<>(); textAttributes.put(TextAttribute.FAMILY, TEST_FONT); textAttributes.put(TextAttribute.SIZE, TEST_FONT_SIZE); textAttributes.put(TextAttribute.WEIGHT, TEST_FONT_WIDTH); if(TEST_KERNING) textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON); if(TEST_LIGATURES) textAttributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON); Font font = Font.getFont(textAttributes); JTextField txtInput = new JTextField(); txtInput.setFont(font); JFrame frame = new JFrame("Demo"); frame.setSize(400, 100); frame.add(txtInput); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); SwingUtilities.invokeAndWait(() -> frame.setVisible(true) ); } }
内容的提问来源于stack exchange,提问作者calaedo
相关产品推荐
相关产品推荐

