OpenJDK与OracleJDK的字体略宽

我注意到使用OpenJDK与OracleJDK相比字体间距的差异。 我把它缩小到了字体。 它们由OpenJDK呈现得稍微宽一点……仔细目视检查上面的截图显示字符宽度是相同的,唯一的区别是间距。 我还通过对所有字符A-Za-z0-9的字体度量的编程检查来确认这一点。

OpenJDK vs OracleJDK字体

例如,12pt的字符串“Dialog – plain”是

  • OpenJDK中125px宽 – 我的8u131-b11版本
  • OpenJDK宽125px – 来自redhat磁盘的库存RPM – 1.8u45-b13
  • OracleJDK中的120px宽度 – 来自Oracle网站的8u131-b11版本

我已经广泛搜索了这方面的信息,并从Java_Runtime_Environment_fonts找到了各种选项,包括-Dawt.useSystemAAFontSettings-Dswing.useSystemFontSettings-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeel 。 我试过改变所有这些,但结果保持不变。

进一步调查发现sun.font.FontScaler ,它使用不同的底层fontscaler。 这在sun.font.FontUtilities可以部分配置,它检查-Dsun.java2d.font.scaler=t2k的系统属性,但设置此项没有区别。

我的问题:可以将FreetypeFontScaler配置为以类似或更接近T2KFontScaler的方式运行吗?

 if (FontUtilities.isOpenJDK) { scalerClass = Class.forName("sun.font.FreetypeFontScaler"); } else { scalerClass = Class.forName("sun.font.T2KFontScaler"); } 

这是我一直在使用的测试程序

 public class FontTester { public static void main(String[] args) throws Exception { System.out.println(String.format("java.home=%s", System.getProperty("java.home"))); String family = Font.DIALOG; int style = Font.PLAIN; describeFont(new Font(family, style, 12)); JFrame frame = new JFrame(); frame.setSize(800, 600); frame.add(new DemoPanel()); frame.setVisible(true); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); } private static class DemoPanel extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); String family = Font.DIALOG; int style = Font.PLAIN; Font font = new Font(family, style, 20); g.setFont(font); String str = family + " - " + name(font) + " "; Rectangle2D bounds = g.getFontMetrics().getStringBounds(str, g); str += String.format("%fx %f", bounds.getWidth(), bounds.getHeight()); g.drawString(str, 10, 50); } private String name(Font font) { List attrs = new ArrayList(); if (font.isBold()) { attrs.add("bold"); } if (font.isItalic()) { attrs.add("italic"); } if (font.isPlain()) { attrs.add("plain"); } return String.join(",", attrs); } } private static void describeFont(Font font) throws Exception { Method method = Font.class.getDeclaredMethod("getFont2D"); method.setAccessible(true); Font2D font2d = (Font2D) method.invoke(font); System.out.print(String.format("%s: ", font)); describeFont2D(font2d); } private static void describeFont2D(Font2D font) { if (font instanceof CompositeFont) { CompositeFont cf = (CompositeFont) font; for (int i = 0; i  %s \n", font)); } } } 

然而,更多的调查已经追溯到sun.font.FontStrike.getGlyphMetrics(int)返回不同的结果。 对于glyph-id 39(“D”),使用Oracle JDK(通过T2KFontScaler)将advance X值返回为14.0px,使用OpenJDK(通过FreetypeFontScaler)返回15.0px

为了确定哪个是“正确的”,我使用fontbox Java解析器从TTF文件LiberationSans-Regular.ttf中的HMTX表中提取Glyph-ID 39的advance X值。 该值为1479个字体设计单位 – 以20pt字体大小映射到14.44px。

下一个字符“i” – glyph-id 76没有区别。根据fontbox,这是4.44,T2KScaler和FreetypeScaler都返回4

我进一步将其缩小到使用标准“非小数”度量标准时使用的舍入/计算。 如果启用了小数度量标准,那么Oracle和Open JDK的行为都相同,尽管宽度仍然比Oracle非小数情况略小。

 g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 

进一步调查发现sun.font.FontScaler,它使用不同的底层fontscaler。 这在sun.font.FontUtilities中可以部分配置,它检查-Dsun.java2d.font.scaler = t2k的系统属性,但设置此项没有区别。

你是正确的,因为底层字体缩放器在Oracle和OpenJDK之间是不同的 – 不幸的是,这是硬编码的,不可配置。

相关代码在FontScaler:97中 :

 if (FontUtilities.isOpenJDK) { scalerClass = Class.forName("sun.font.FreetypeFontScaler"); } else { scalerClass = Class.forName("sun.font.T2KFontScaler"); } 

isOpenJDK标志? 它由FontUtilities设置:125 :

 File lucidaFile = new File(jreFontDirName + File.separator + LUCIDA_FILE_NAME); isOpenJDK = !lucidaFile.exists(); 

那个常数:

 static final String LUCIDA_FILE_NAME = "LucidaSansRegular.ttf"; 

除非我遗漏了那些源文件中的内容,否则没有其他配置标志或任何其他条件会改变正在使用的缩放器类。

因此,唯一含糊不清的方法(我在这里排除可怕的类加载器/reflection黑客)就是将Lucida文件添加到位。 不幸的是,虽然在大多数情况下我都能想到(假设你没有将JRE与软件包一起分发),这也不是一个可行的解决方案。