OpenJDK与OracleJDK的字体略宽

Ada*_*dam 13 java openjdk

我注意到使用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版本

我已经广泛地搜索信息就这个问题和发现的各种选项,包括-Dawt.useSystemAAFontSettings,-Dswing.useSystemFontSettings,-Dswing.defaultlaf=com.sun.java.swing.plaf.gtk.GTKLookAndFeelJava_Runtime_Environment_fonts.我试过改变所有这些,但结果保持不变.

进一步的调查发现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");
}
Run Code Online (Sandbox Code Playgroud)

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

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("%f x %f", bounds.getWidth(), bounds.getHeight());
            g.drawString(str, 10, 50);
        }

        private String name(Font font) {
            List<String> 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 < cf.getNumSlots(); i++) {
                PhysicalFont pf = cf.getSlotFont(i);
                describeFont2D(pf);
                break;
            }
        } else {
            System.out.print(String.format("-> %s \n", font));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,更多的调查追溯了这一点,但却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);
Run Code Online (Sandbox Code Playgroud)

Mic*_*rry 7

进一步的调查发现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");
}
Run Code Online (Sandbox Code Playgroud)

还有isOpenJDK国旗?由FontUtilities:125设置:

File lucidaFile = new File(jreFontDirName + File.separator + LUCIDA_FILE_NAME);
isOpenJDK = !lucidaFile.exists();
Run Code Online (Sandbox Code Playgroud)

那个常数:

static final String LUCIDA_FILE_NAME = "LucidaSansRegular.ttf";
Run Code Online (Sandbox Code Playgroud)

除非我错过了这些源文件中的某些内容,否则没有其他配置标志或任何其他条件会更改所使用的缩放器类。

因此,做到这一点的唯一隐约明智的方法(我在这里不包括可怕的类加载器/反射黑客)是在适当位置添加该Lucida文件。不幸的是,尽管在大多数情况下我都可以想到(假设您不随包一起分发JRE),但这也不是一个可行的解决方案。

  • 感谢您的关注。我调查了您先前提出的建议-放置Lucida字体使OpenJDK像Oracle JDK一样“看起来”,不幸的是T2KFontScaler由本机libt2k.so支持。这是封闭的源代码,我对仅从Oracle发行版中复制一个许可证授权的含义感到谨慎。 (2认同)