macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Vertical Text in Java

by Lee Ann Rucker
03/22/2002

If you're developing your application using Java on the Macintosh, you may have noticed that Apple's Human Interface Guidelines specify vertical text in side tabs, but Java only supports horizontal text.

To address this, I wrote the VTextIcon class, which manages vertical text with support for localization. VTextIcon does so using only the Swing and Graphics2D APIs, so you should be able to use the VTextIcon class on any Java2 platform, and in any JComponent which supports Icons.

This article will help you learn how to display vertical text in a Swing component such as a JTabbedPane. It is written for intermediate-level programmers.

The VTextIcon Class

Here are the methods you'll use to create and manage a VTextIcon:

	
public VTextIcon(Component component, String label) {
		this(component, label, ROTATE_DEFAULT);
	}

 	public VTextIcon(Component component, String label, 
				 int rotateHint) {
		fComponent = component;
		fLabel = label;
		fRotation = verifyRotation(label, rotateHint);
		calcDimensions();
		fComponent.addPropertyChangeListener(this);
	}

	public void setLabel(String label) {
		fLabel = label;
		// Make sure the current rotation is still legal
		fRotation = verifyRotation(label, fRotation); 
		recalcDimensions();
	}
	
Comment on this articleWhat questions do you have for Lee Ann about her VTextIcon class?
Post your comments

You provide a Component so that VTextIcon knows which Font to use when calculating the string dimensions for getIconWidth and getIconHeight. It also invalidates the Component's layout when the dimensions change, either because you used setLabel to change the label or because the Component's font changed.

The verifyRotation method determines the best rotation based on the rotation style from rotateHint and the characters in the label. If your label doesn't support the style in rotateHint, verifyRotation will return the default.

VTextIcon supports three rotation styles:


	ROTATE_NONE
	ROTATE_LEFT
	ROTATE_RIGHT

Along with:

	ROTATE_DEFAULT

Note that not all styles are available for all scripts. Roman, for instance, can be drawn in any style, but Chinese, Japanese, and Korean (CJK) cannot be rotated, and Arabic, Hebrew and Syriac should be rotated.

While Unicode doesn't define the behavior for vertical text -- considering it a "formatting style” --- it does describe common approaches:

When setting text using the Arabic script in vertical lines, it is more common to employ a horizontal baseline that is rotated by 90º counterclockwise so that the characters are ordered from top to bottom. Latin text and numbers may be rotated 90º clockwise so that the characters are also ordered from top to bottom.

So the default for Roman is ROTATE_RIGHT and for Arabic ROTATE_LEFT, though they can be rotated in either direction.

If Roman text isn't rotated, it should be centered Japanese for "Japanese"
It would be difficult to read if it was rotated
Here is the Arabic word for beach (shatt), showing the individual letters and the entire word. Arabic letters take on different forms depending on their position in the word.

Arrow shows reading direction -- Unicode recommends top-to-bottom for all scripts

calcDimensions determines the width and height, and also checks for special Japanese characters, as described in the next section. recalcDimensions is used after the VTextIcon has been created; it calls calcDimensions, then checks whether the size has changed, and if so invalidates the Component's layout.

Japanese and Small Kana

In addition to the ideographs ("kanji"), Japanese has two syllabaries, known as "kana." Most syllables are represented by a single kana, but some are represented by two. In these cases the second kana is drawn slightly smaller than the normal characters, though it takes up the same amount of space. When drawn horizontally, the small kana should be in the bottom-left quadrant of the space, but when drawn vertically it goes in the top-right. Additionally, vertical punctuation is shifted to the far top-right corner. Since the VTextIcon can't tell Java that the kana is being drawn vertically, it has to shift the glyph into the correct location in paintIcon.


Vertical and horizontal small kana

Mixed-Language Strings

Because the rotated text is drawn using Graphics2D.rotate, it's rendered by Java as if it were horizontal. This may cause undesired effects in mixed-language strings. Unicode has this to say on mixed Arabic and Latin:

The bidirectional algorithm also comes into effect when some characters are ordered from bottom to top. For example, this happens with a mixture of Arabic and Latin glyphs when all the glyphs are rotated uniformly 90º clockwise. (The choice of whether text is to be presented horizontally or vertically, or whether text is to be rotated, is not specified by the Unicode Standard, and is left up to higher-level protocols.)

It's common for mixed CJK/Roman to rotate the Roman string, but VTextIcon does not support mixed rotation styles. The implementation of mixed-language strings is left as the proverbial exercise for the reader.

CompositeIcon

Since you're using your JComponent's Icon for text, does this mean you can't have a graphical Icon? No. That's what CompositeIcon does for you -- it takes two Icons (of any kind) and draws them with a specified position relative to each other and orientation within the allotted space.

/**
 CompositeIcon is an Icon implementation which draws two icons 
 with a specified relative position:
 LEFT, RIGHT, TOP, BOTTOM 
	specify how icon1 is drawn relative to icon2
 CENTER
	icon1 is drawn first, icon2 is drawn over it
 
 and with horizontal and vertical orientations 
 within the allotted space
 
 It's useful with VTextIcon when you want an icon with your text: 
	if icon1 is the graphic icon and icon2 is the VTextIcon, 
	you get a similar effect to a JLabel 
	with a graphic icon and text
	*/

Creating a JTabbedPane With a VTextIcon

To create tabs with vertical text, first create a JTabbedPane with LEFT or RIGHT tabs:

	JTabbedPane panel = new JTabbedPane(LEFT);

Then create a VTextIcon for that panel. We'll use the default orientation here:

	VTextIcon textIcon = new VTextIcon(panel, "Mac OS X");

The test application uses one of the FileView icons, but any icon will do:

	Icon graphicIcon = UIManager.getIcon("FileView.computerIcon");

Now use a CompositeIcon to combine them:

	CompositeIcon icon = new CompositeIcon(graphicIcon, textIcon);	

References

Aqua Human Interface Guidelines

Unicode Writing Directions FAQ

Unicode Standard Annex #9: The Bidirectional Algorithm

Small kana writing tutorial

Arabic language and writing tutorial

Glyph metrics and vertical layout

Finally, create the tab, passing in "null" for the title, the CompositeIcon, and the Component to be displayed when this tab is active (makePane is a placeholder in the test application):

	panel.addTab(null, icon, makePane());

If you only want to display text, just use the VTextIcon in addTab:

	panel.addTab(null, textIcon, makePane());

Summary

By using VTextIcon and CompositeIcon, you can provide side tabs with vertical text without losing any of the functionality of the standard tabs with graphic icons and horizontal text.

Sources

You can find the complete sources for VTextIcon.java, CompositeIcon.java, and a sample application, Test.java here.