Related Reading:![]() Java Internationalization |
|||||
Internationalization (I18N) and Localization is easier with Java, in contrast with other, previous native applications. Credit goes to IBM's Center for Java Technology, Silicon Valley.
Although it is much easier to use I18N and L10N due to the different approaches by Sun and Microsoft, we are faced with new challenges. On one hand we want to target a wider audience, without maintaining two sets of code (using the Java "write once, run anywhere" vision). On the other, we want to use as many features as possible in the latest Java release. Of course, there is a limit to what we can achieve.
In this article, we will look at employing the Java reflection so that our Java applet utilizes system fonts found in Java SDK 1.3 as well as the MS VM implementation, while not causing backward compatibility problems for Navigator 4.76 with Java 1.1.5. With this technique, we will be able to solve the problem of Chinese, Japanese, and Korean characters displayed as "blocks" in IE with MS VM.
The display of these characters with an English language OS in
Navigator 4 requires the necessary changes to the
font.properties file on a client machine. As for
Navigator 6 and IE, we just need to run the test applet and use the
correct font in our applet. That's it.
In JDK 1.1, font mapping between the Java logical fonts and system
physical fonts is achieved by making the necessary changes in
font.properties, which lists the font results only by
logical font names.
The situaion is much better now with JDK 1.3. We can get a list of the user's system TrueType fonts or create it ourselves. Similarly, Microsoft makes it possible to do this with their own implementation. Due to the different implementations of Sun and Microsoft, how can we provide a uniform access to these different solutions?
The solution:
Java reflection and a helper class NEFontFactory uses the Factory pattern. Before we can do this, we need to
In JDK 1.1, we cannot access the system font listing except logical fonts
names. These logical fonts are mapped to the system physical fonts by
editing the font.properties found in the
..\lib directory (See Creating
font.properties.en for Netscape 4).
In JDK 1.1, the font listing API returns a list of logical font names:
java.awt.Toolkit.getDefaultToolkit().getFontList() // deprecated since JDK1.2
In JDK 1.3, the listing of system fonts is available using the following:
java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()
In MS VM, the listing of system fonts is available using the following:
com.ms.awt.FontX.getFontList()
In JDK 1.1, we cannot create the TrueType font using its name.
Instead we create the font using a logical font name, which is
mapped to a TrueType font in the font.properties.
Now in JDK 1.3, we can create the TrueType font simply by specifying its name:
new Font(fontName, fontStyle, fontSize)
In MS Java, the system font is obtained via com.ms.awt.FontX:
public static Font getFont(String face, int style, int size, int flags);
public static Font getFont(String face, Vector v, int size);
For your reference, the following hardware and software configurations is recommended for use in our development environment.
Hardware
Software
Getting it
One way is to install it from the MultiLanguage
Pack from Office 2000.
For a listing of fonts provided by the MultiLanguage Pack (including the code pages and the languages they support), check out Administering Fonts in an International Office.
Otherwise, let IE do the job for you. To download Simplify Chinese font, simply access Chinese language News paper web site in Singapore using IE. IE will prompt you to download the necessary font in order to view the page. For the following fonts, try the sites below:
We may be able to view the page in Chinese, but what font is IE using to display the page?
|
If you have IE (5.0 through 5.5), it is easy to find the font used to display a particular language. For example, I would like to find out the fonts available for displaying Chinese web pages. I can do the following:
Access the IE menu: Tools/Internet Options/General/Fonts (see Figure 1). Then use the following fonts to display Simplified Chinese text.
|
Due to the large character sets of many Asian languages, their font file is larger than the character sets typical of most Western languages. With this knowledge, we can sort the font listing based on file size as shown in Figure 2 from Control Panel/Font.
|
From the figure shown above, one or many of the above fonts "Batang", "Bitstream Cyberbit", "SimSun & NsimSun", "SimHei", and "MS Song" can be used to view Chinese characters. We can use Navigator 4 to test which particular font is suitable.
In Navigator 4, go to Edit/Preferences/Appearance/Fonts, and set the preferences as shown in the following figure.
|
With the completion of the above setting, access the site http://www.zaobao.com to view the text correctly when that particular font is used. In this case, the SimSun font can be used to display Chinese characters.
Yet another way of knowing what font can be used to display native text is to use Office 2000 for opening the native text file. It will automatically detect the encoding, then format the text with the necessary font.
With the font on your system and the knowledge of what font name to
use, we can create font.properties.en for Navigator 4 so
that our Java applet will display Chinese characters.
First, create a copy of font.properties.zh file found
in Netscape's ..\classes directory. Then rename the file
font.properties.en in the same directory.
font.properties.zh for Netscape 4 can be found at
C:\program files\netscape\communicator\program\java\classes
Edit the font.properties.en by replacing all occurrences of
\uXXXX unicode strings related to the SimSun font. In our case, replace
\u5b8b\u4f53 with SimSun.
With this new font.properties.en your Navigator 4, we will be able to display the Chinese characters when we use any of the logical fonts in our Java applet.
We can make changes to the original font.properties
too. However, it is easier to distribute font.properties.en,
so that we aren't required to backup files.
Now we want to create a helper class, called NEFontFactory, which will have the following features:
The Helper class makes use of a Factory Pattern to create Font objects.
|
By detecting the Java version and vendor, we ensure that our code
will always work. We will not encounter unnecessary exceptions when we
obtain Class objects for either GraphicsEnvironment or
FontX class.
We can obtain the Java version and vendor as follows:
String vendor = System.getProperty("java.vendor");
String version = System.getProperty("java.version");
The following table shows the vendor values and versions on different browsers.
| Netscape Navigator 4.76 | Netscape Navigator 6 | IE | |
| Vendor | Netscape Communications Corporation | Sun Microsystems Inc. | Microsoft Corp. |
| Version | 1.1.5 | 1.3.0_01 | 1.1.4 |
With Java reflection, we ensure that we are able to compile our NEFontFactory class using any version of the Java compiler, since we do not need to import any Java version specific code. For more information on Java Reflection, check out Sun's Java Tutorial.
The first step is to retrieve the Class object. With the knowledge
of Java version and vendor, we obtain our FontX and
GraphicsEnvironment Class objects with the following
method calls for IE VM and JDK 1.3, respectively.
Class msFontX = Class.forName("com.ms.awt.FontX"); // Class object for IE environment
Class graphicEnv = Class.forName("java.awt.GraphicsEnvironment"); // JDK 1.3 environment
Next, we will look at invoking methods. In the MS SDK Java doc, we find the following documentation.
FontX Class
The FontX Class of the com.ms.awt package creates a font object that defines the font family, font face, style, and size. The FontX class also supports TrueType fonts.
public class FontX extends FxFont
{
// Fields
public static final int EMBEDDED;
public static final int OUTLINE;
public static final int STRIKEOUT;
public static final int UNDERLINE;
// Constructors
public FontX();
public FontX(String name, int style, int size);
public FontX(String name, int style, int size, boolean bEmbed);
public FontX(String name, int style, int size, int xFlags);
// Methods
public boolean equals(Object obj);
public static String[] getAttributeList();
public static String[] getAttributeList(String face);
public int getFlags();
public static int getFlagsVal(String face, String attribute);
public static Font getFont(String face, int style, int size, int flags);
public static Font getFont(String face, Vector v, int size);
public static String[] getFontList();
public static int getStyleVal(String face, String attribute);
public boolean isTypeable(int language);
public static boolean matchFace(String face);
public String toString();
}With JDK 1.1, fonts are mapped with a font properties file. So, we can obtain different fonts by editing this file. However, the Microsoft implementation does not support this type of font association. Therefore, we can obtain desired fonts with FontX objects, which are created with Win32 font names.
FontX field definitions EMBEDDED Indicates the new font should be linked to a privately embedded font. Note This is only significant for fonts that are loaded by non-Java objects. Fonts that are loaded by Java objects are automatically embedded.
OUTLINE Indicates that the text drawn by using the new font should be drawn by using the outline of the glyph. This is only supported by certain fonts in the system, and only GraphicsX will support this feature. Trying to draw outline fonts using a Win32Graphics object will result in the glyph being drawn normally.
STRIKEOUT Indicates that text drawn using the new font should be struck through.
UNDERLINE Indicates that text drawn using the new font should be underlined.
We are interested in getFont(String face, int style, int
size, int flags) and getFontList() in
FontX.
We get the available font listing and Font object from the
FontX class using reflection.
Getting a font listing:
Method getFontListApi = msFontX.getMethod("getFontList", null);
// need to perform sorting of the listing .... (see Exercise)
String fontListing[] = (String[])getFontListApi.invoke(null, null);
Creating a Font:
String fname = new String(fontName);
Class[] param = new Class[4];
param[0] = fname.getClass();
param[1] = Integer.TYPE;
param[2] = Integer.TYPE;
param[3] = Integer.TYPE;
Method getFontApi = msFontX.getMethod("getFont", param);
Field field = msFontX.getField("EMBEDDED");
Integer fstyle = new Integer( style );
Integer fsize = new Integer( size );
Integer fflags = (Integer)field.get( null ); // null because it is a static field.
Object[] args = { fname, fstyle, fsize, fflags};
Font f = ( Font ) getFontApi.invoke(null, args ); // null because it is a static method.
For Sun Java VM (1.1 and above), the Font object is created normally,
new Font(fontName, style, size);
while the font listing is obtained via Java Reflection for Java 1.3:
Method getLocalGraphicsEnvironmentApi =
graphicEnv.getMethod("getLocalGraphicsEnvironment", null);
// invoke the API to obtain an instance of GraphicsEnvironment
Object GEInstance =
getLocalGraphicsEnvironmentApi.invoke(null, null); // static method with no parameters
Method getAvailableFontFamilyNamesApi =
graphicEnv.getMethod("getAvailableFontFamilyNames",null);
String fontListing [] =
(String[])getAvailableFontFamilyNamesApi.invoke(GEInstance, null);
We know that object creation takes up a fair bit of time, and so
font caching is added to the NEFontFactory class to
improve the performance of Font object creation.
Caching is implemented using java.util.Hashtable,
where the key is formed using fontName+style+size.
We place a Font object into Hashtable by the following (where
f is the Font object):
String name = f.getName();
int style = f.getStyle();
int size = f.getSize();
cache.put( name+style+size, f);
Now we can obtain a Font object by calling
public static Font NEFontFactory.getFont(Sting fontName, int style, int size)
And for a listing of font names available on your system, we can call
public static String[] NEFontFactory.getFontList()
|
An applet was created as shown in Figure 4, with the following applet parameters.
The applet reads the file with the specified encoding, then displays
the text using the preferred font name and size. The
country and lang parameters are used to create the
java.util.Locale object. The text is displayed using
java.awt.Canvas. Line wrapping is performed using
java.text.BreakIterator.
What follows are some observations when the demo applet is executed on IE, Navigator 4.76, and 6 for Chinese, Japanese, and Korean text.
Encoding in IE
IE does not support every encoding
supported in the JDK. For example, if you try reading a native Korean
character in our test applet you will encounter the following:
UnsupportedEncodingException
In order to display Korean characters correctly in all browsers, we need to do some preprocessing. We need to perform transcoding, where the Korean text file is transformed from one encoding to another; in this case, EUC-KR to UTF8. Transcoding can be implemented easily in Java. We read in the data in one encoding and write out the data in another encoding.
After transcoding the Korean text files to UTF8, we specify the encoding parameter in the applet as UTF8. Then use the preferred font name "Batang" to display Korean characters in the applet.
|
IE Java VM
If the Font object is not created with
FontX class but, rather, with
new Font("system font name")
then our Korean characters will be displayed as follows on IE Java VM, using the "Batang" and "Dialog" font names.
|
|
Netscape Navigator 6 JDK 1.3
Navigator 6 with Sun JDK
1.3 VM works well, except for updated fonts with only a change of font
style.
For example, if the current Font setting in the applet is
Font name = 'Batang'
Font size = 14
Italic = No
Bold = No
and we want to have the same font name, size, but with italic rather than bold, then if we just check the Italic checkbox, and depress the 'Update' button, it will not work.
Instead, we need to set Font Size to any value other than 14 (the current value), and set Italic to "Yes". Then depress the "Update" button. Then set Font Size back to 14. Depress the "Update" button again. You can then get what you wanted as follows:
Font name = 'Batang'
Size = 14
Italic = Yes
Bold = No
On IE and Navigator 4.76 (assuming the necessary changes to
font.properties.en), we do not have this problem.
When we run the Korean character demo in Navigator 6, the loading of the applet took much longer to load than with IE. It may be due to the size of this TrueType Font (15157KB) (see Figure 2). Another reason is likely due to IE being integrated with the OS.
|
You may now want to consider the following suggestions to
NEFontFactory class.
Since the font listing returns by getFontList() of IE
VM are not sorted alphabetically, as compared to
getAvailableFontFamilyNames() in Java 1.3. Therefore, we
may want to sort the result on IE VM. If we do want the code to be
internationalized, we will need to make use of the java.text.Collator
and java.util.Locale information.
Another exercise is to use Java Reflection with the following API found in Java 1.3, where Font object is replicated with just a change of size or style or both.
public Font deriveFont(float size)
public Font deriveFont(int style, float size)
public Font deriveFont(AffineTransform trans)
public Font deriveFont(int style, AffineTransform trans)
public Font deriveFont(int style)
public Font deriveFont(Map attributes)
The latest MS VM can be obtained from Microsoft's Java site.
References on I18N and Java
In this article, we illustrated how to achieve the display of Asian characters in applets on IE by using Java Reflection. Otherwise, these texts would have appeared in "block" characters. We also illustrated how to obtain a listing of user system fonts depending on which version of VM you are using.
This technique has been implemented in our client-side Java-based product, Facado, which in addition to being internationalized, offers interactive and collaborative computing capabilities in the browser.
I welcome your feedback and exchange of ideas. Contact me via email.
I would like to thank Steve Anglin, Managing Editor of ONJava.com, for giving me this opportunity to share this information with you and NexusEdge Technologies Pte Ltd for providing the environment to develop leading-edge Java solutions.
I would like to thank my colleague, Thierry Bedos, for his valuable discussion on Java reflection and feedback on this article.
Jaric Sng is a Java GUI designer and developer with interest and expertise with Java Internationalization.
Return to ONJava.com.
Copyright © 2009 O'Reilly Media, Inc.