1
votes

I'm using Java 10, and swing. I'm using the DejaVu Sans Mono font, because I think it looks nice. However, it has very poor support of CJK characters, so to maximize support, I thought of using Noto Sans CJK as a backup. Although I could use Noto Sans for Latin characters too, I don't quite like their Latin characters too much.

Although this seems like a trivial question, I can't seem to find out how to do it. Even if you can't answer, I appreciate pointers.

TL;DR:

My Problem

I can't display Chinese, Japanese or Korean characters with DejaVu Sans Mono

My Solution

Use Noto Sans CJK

The Problem with my solution

Noto Sans is ugly with Latin, and doesn't support Arabic

My Question

How do I tell a JComponent that if it can't find a character in Font One (DejaVu), it should use Font Two (Noto) instead?

1
Stackoverflow is infamous for these "answers", so I'd like to ask in advance that you don't answer with "Just use Arial" or similar. Although this is a possible solution, (1) it doesn't answer my question, and (2) what if I don't like Arial, or am unhappy with the double-diacritics bug? This like answering "Just speak English" in a post about learning Spanish.Vincent Bechmann
1) That first comment contains information that should be in the question above. Please edit to add it, then delete the comment. 2) Is the text used for the components fixed and unchanging for the run of the app? If so, check(a) the font for (ability to display) the relevant characters when making the components (and setting the text). a) Methods like canDisplayUpTo(String).Andrew Thompson

1 Answers

1
votes

One way is to check the font for the ability to display the relevant characters when making the components (and setting the text).

This can be achieved using methods like Font.canDisplayUpTo(String).

E.G.

enter image description here

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);
    }
}