【问题标题】:Fonts slightly wider in OpenJDK vs OracleJDKOpenJDK 与 OracleJDK 中的字体稍宽
【发布时间】:2018-06-01 18:04:56
【问题描述】:

我注意到与 OracleJDK 相比,使用 OpenJDK 的字体间距存在差异。我已将其范围缩小到字体。它们由 OpenJDK 渲染得稍微宽一些……仔细观察上面的屏幕截图,可以看出字符宽度是相同的,唯一的区别是间距。我还通过对所有字符 A-Za-z0-9 的字体度量进行编程检查来确认这一点。

例如12pt 处的字符串“Dialog - plain”是

  • OpenJDK 中 125px 宽 - 我构建的 8u131-b11
  • OpenJDK 中 125 像素宽 - 来自 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,这使用了不同的底层字体缩放器。这似乎在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("%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));
        }
    }
}

更多的调查已经追踪到 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 的提前 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);

更新 - 2020 年 1 月 * 在 Java 11 中有一个可能相关的新选项 - 请参阅 https://bugs.openjdk.java.net/browse/JDK-8217731

FREETYPE_PROPERTIES=truetype:interpreter-version=35

【问题讨论】:

  • 我认为你想要实现的目标是不可能的,因为 Oracle Java 和大多数 OpenJDK 版本中的字体渲染库不同 - mail.openjdk.java.net/pipermail/discuss/2017-November/…
  • 很可能不是这样:但由于 DIALOG 是一种逻辑字体,您最好先检查特定的非 JDK 字体,或者探索字体是否确实相同。
  • 它可能比“稍微宽一点”还要糟糕。我有一个“6”,看起来非常像“8”。来自我的 +1。

标签: java


【解决方案1】:

进一步调查发现sun.font.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";

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

因此,这样做的唯一模糊合理的方法(我在这里排除可怕的类加载器/反射黑客)是添加该 Lucida 文件。不幸的是,尽管在大多数情况下我能想到(假设您没有将 JRE 与包一起分发),但这也不是一个真正可行的解决方案。

【讨论】:

  • 感谢您查看此内容。我调查了您之前的建议 - 放置 Lucida 字体以使 OpenJDK“看起来”像 Oracle JDK,不幸的是,T2KFontScaler 由不存在的本机 libt2k.so 支持。这是封闭源代码,我对仅从 Oracle 发行版复制它的许可含义持谨慎态度......
  • @Adam 是的,那是正确的 - 我实际上有相同(或类似)的问题报告了我不久前开发的东西,并得出了相同的结论(除非我想要潜在的法律问题,否则我不可能真的只是复制这些文件并完成它。)不幸的是,我认为没有简单的解决方案。
  • 有一个我还没有探索过的解决方案......因为我的调查显示差异归结为与 TTF 文件不同的“advance X”值......我可以创建一组新的经过处理的 TTF 文件,只有 OpenJDK 使用这些文件,并将“fudge factor”应用于“advance X”,这样渲染的输出大致相同——这可能看起来很疯狂,但与调整 100+ 布局相比成本旧版 UI 屏幕会少很多 :)
  • @Adam 这确实看起来很疯狂,但我非常感谢维护遗留软件经常要求不时采取疯狂的方法!
  • @Adam 出于兴趣,您是否曾经实施过新的“捏造”TTF 文件集?好奇这是否有帮助!
猜你喜欢
  • 1970-01-01
  • 2017-03-09
  • 2022-01-05
  • 1970-01-01
  • 2020-04-13
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
  • 2013-01-09
相关资源
最近更新 更多