Roy Tang

Programmer, engineer, scientist, critic, gamer, dreamer, and kid-at-heart.

Blog Notes Photos Links Archives About

We have an Applet that can possibly display Chinese text. We are specifying a font for it (Arial), it works fine under both Windows and Mac OSX.

But in Firefox on Linux the Chinese characters are rendered as squares. Is there a way to work around this? Note that we can’t assume the existence of a particular font file on the client.

Comments

This indicated that the font does not support Chinese characters (which you probably guessed).

You might find the java.awt.Font.canDisplayUpto() method interesting.

http://www.j2ee.me/javase/6/docs/api/java/awt/Font.html#canDisplayUpTo(java.lang.String)

“Indicates whether or not this Font can display a specified String. For strings with Unicode encoding, it is important to know if a particular font can display the string. This method returns an offset into the String str which is the first character this Font cannot display without using the missing glyph code. If the Font can display all characters, -1 is returned.”

That’s because Arial on Windows and Mac are all Unicode font but it only has Latin-1 charset on Linux. On many Linux distributions, Chinese fonts are optional and there may not be Chinese font available.

A common technique is to searching through all your font to see any of them can display Chinese characters. For example,

static final Font defaultFont =new Font( "Arial Unicode MS", Font.BOLD, 48 );

static private Font[] allFonts;

static final char sampleChineseCharacter = '\u4F60';  // ni3 as in ni3 hao3

public static void loadFonts() {

    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();

    allFonts = env.getAllFonts();
    int nFonts = allFonts != null ? allFonts.length : 0;
    fontNames = new String[nFonts];
    fontMap = new Hashtable();
    String currentFamily = "";
    int j = 0;

    for ( int i = 0; i < nFonts; i++ ) {

        Font font = allFonts[ i ];

        System.out.println( allFonts[ i ] );

        if ( font.canDisplay( sampleChineseCharacter )) {

                currentFamily = font.getFamily();

                Object key = fontMap.put( currentFamily, font );

                if ( key == null ) {

                    // The currentFamily hasn't been seen yet.

                    fontNames[ j ] = currentFamily;
                    j++;

                }

        }

    }

    String tmp[] = fontNames;
    fontNames = new String[j];
    System.arraycopy( tmp, 0, fontNames, 0, j );

}
You might have to pass the following param in the object tag: <param name="java_arguments" value="-Dfile.encoding=utf-8" />

I found the code here inadequate for my needs.

I needed to test an unknown input string, to determine what font to use, hence, I needed to check every single character. (see below)

By the way, the font.canDisplayUpTo method will not work. It may approve a font, that can only display some of the characters.

So, just use this code below.

Font[] allFonts;
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();

allFonts = env.getAllFonts();

Font targetFont = null;
for ( int i = 0; i < allFonts.length; i++ ) 
{
    Font font = allFonts[ i ];
    boolean canDisplayAll = true;
    for (char c : text.toCharArray())
    {
        if (!font.canDisplay(c))
        {
            canDisplayAll = false;
            break;
        }
    }
    if (canDisplayAll)
    {
        logger.debug("font can display the text " + font.getName());
        targetFont = font;
        break;
    }
    else
    {
        logger.debug("cant display " + font.getName());
    }
}