如何设置 "backup" 字体

How to set a "backup" font

我正在使用 Java 10,挥杆。我使用的是 DejaVu Sans Mono 字体,因为我觉得它看起来不错。但是,它对 CJK 字符的支持很差,所以为了最大限度地支持,我想到了使用 Noto Sans CJK 作为备份。虽然我也可以用 Noto Sans 来写拉丁字符,但我不太喜欢他们的拉丁字符。

尽管这似乎是一个微不足道的问题,但我似乎无法找到解决方法。就算答不上来,也请指点。

长话短说:

我的问题

我无法使用 DejaVu Sans Mono 显示中文、日文或韩文字符

我的解决方案

使用 Noto Sans CJK

我的解决方案存在问题

Noto Sans 不支持拉丁语,不支持阿拉伯语

我的问题

我如何告诉 JComponent 如果它在字体一 (DejaVu) 中找不到字符,它应该改用字体二 (Noto)?

一种方法是在制作组件(和设置文本)时检查字体是否能够显示相关字符。

这可以使用 Font.canDisplayUpTo(String).

等方法来实现

import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.util.*;

public class FallbackFont {

    private JComponent ui = null;
    private final String[] fontFamilies = GraphicsEnvironment.
            getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    String[] examplesTexts = {
        "The quick brown fox jumps over the lazy dog.", // English
        "\u6253\u5370\u8FC7\u671F\u8BC1\u4E66\u8BB0\u5F55", // Chinese
        "\u0627\u0644\u0633\u0644\u0627\u0645 "
        + "\u0639\u0644\u064A\u0643\u0645", // Arabic
        new String(Character.toChars(128176))
    };
    String[] preferredFonts = {
        "DejaVu Sans Mono",
        "Microsoft JhengHei",
        "Noto Sans CJK TC Black", // has strange display problem here .. 
    };

    FallbackFont() {
        initUI();
    }

    private HashMap getCompatibleFonts(String text) {
        HashMap cF = new HashMap<>();
        for (String font : fontFamilies) {
            Font f = new Font(font, Font.PLAIN, 1);
            if (f.canDisplayUpTo(text) < 0) {
                cF.put(font, f);
            }
        }
        return cF;
    }

    private Font getPreferredFontForText(String text) {
        HashMap compatibleFonts = getCompatibleFonts(text);
        for (String preferredFont : preferredFonts) {
            Font font = (Font) compatibleFonts.get(preferredFont);
            if (font != null) {
                return font;
            }
        }
        Set keySet = compatibleFonts.keySet();
        String firstCompatibleFont = (String) keySet.iterator().next();
        return (Font) compatibleFonts.get(firstCompatibleFont);
    }

    public final void initUI() {
        if (ui != null) {
            return;
        }

        ui = new JPanel(new GridLayout(0, 2, 4, 4));
        ui.setBorder(new EmptyBorder(4, 4, 4, 4));
        for (String text : examplesTexts) {
            Font font = getPreferredFontForText(text);
            JButton b = new JButton(text);
            b.setFont(font.deriveFont(b.getFont().getSize2D()));
            ui.add(b);
            ui.add(new JLabel(font.getName()));
        }
    }

    public JComponent getUI() {
        return ui;
    }

    public static void main(String[] args) {
        Runnable r = () -> {
            FallbackFont o = new FallbackFont();

            JFrame f = new JFrame(o.getClass().getSimpleName());
            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
            f.setLocationByPlatform(true);

            f.setContentPane(o.getUI());
            f.pack();
            f.setMinimumSize(f.getSize());

            f.setVisible(true);
        };
        SwingUtilities.invokeLater(r);
    }
}