// Multi-line fancy output in swing.
// This class displays multiple lines of stylized text.
// Adopted 2002 by Wayne Pollock, Tampa Florida USA, from
// an article published in "Core Java(tm) Technologies Tech Tips"
// (formerly the "Java Developer Connection(sm) (JDC) Tech Tips"),
// July 23, 2002 issue, written by John Zukowski,
// president of JZ Ventures, Inc. (http://www.jzventures.com).
//
// Swing components are usually sufficient for dealing with the
// display of multiline text, but not always. For example, if you
// want to create a custom editor, you'll probably need to draw
// the multiline output yourself. In cases like this, there are
// five classes available to assist you:
//
// * AttributedString
// * AttributedCharacterIterator
// * FontRenderContext
// * LineBreakMeasurer
// * TextLayout
//
// These classes let you describe the text to draw, and measure how
// much text will fit on each line. Use the AttributedString class
// to contain the text. It allows different characters of the
// text string to have different attributes. To configure an attribute
// for just a part of the content, you pass in a beginning and ending
// index:
//
// attributedString.addAttribute(
// TextAttribute.FONT, font, startPosition, endPosition);
//
// The TextAttribute class of the java.awt.font package includes
// constants for the different attributes you can set. These include
// foreground and background colors, font, superscript, underline,
// justification, and many others. When you need to draw the text,
// you fetch an AttributedCharacterIterator from the AttributedString
// so that the system can pull out the individual characters of the
// string.
//
// FontRenderContext is used to ensure that the proper text sizes
// are used in the drawing. You obtain the FontRenderContext from
// the Graphics2D object passed into the paint method.
//
// The LineBreakMeasurer class is used to determine how much text
// fits on a line. More specifically, given an
// AttributedCharacterIterator and FontRenderContext, LineBreakMeasurer
// measures how much of the AttributedString fits on each line.
// Each line is represented by a TextLayout. That just leaves drawing
// the TextLayout, and there is a draw method for just that. Between
// lines, you just need to increase the y position to move down the
// screen.
import java.awt.*;
import javax.swing.*;
import java.awt.font.*;
import java.text.*;
import java.util.*;
public class MultiLineDemo extends JFrame
{
private String text =
"EDWARD by the grace of God, King of " +
"England, Lord of Ireland, and Duke of " +
"Guyan, to all Archbishops, Bishops, etc. " +
"We have seen the Great Charter of the " +
"Lord HENRY, sometimes King of England, " +
"our father, of the Liberties of England, " +
"in these words: ";
private AttributedString attrStr;
private Font font;
private Color color;
public static void main ( String [] args )
{
MultiLineDemo frame = new MultiLineDemo();
frame.centerWin();
frame.show();
}
public MultiLineDemo ()
{
super( "MultiLine Demo" );
setDefaultCloseOperation( EXIT_ON_CLOSE );
font = (Font)UIManager.get( "Label.font" );
font = font.deriveFont( 24f );
color = (Color) UIManager.get( "Label.foreground" );
attrStr = new AttributedString( text );
attrStr.addAttribute( TextAttribute.FONT, font );
attrStr.addAttribute( TextAttribute.FOREGROUND, color );
// A LinkedList preserves the order that items were added,
// so it is a good choice for a list of words to make
// bold. (To have a list of words and different attributes
// for each, you make a class Pair with a word and an attribute,
// then create a list of Pairs.)
LinkedList boldWords = new LinkedList();
boldWords.add( "EDWARD" );
boldWords.add( "King" );
boldWords.add( "Lord" );
boldWords.add( "Duke" );
boldWords.add( "Lord HENRY" );
boldWords.add( "King" );
addAttributes( boldWords );
setSize( 600, 350 );
}
private void addAttributes ( LinkedList boldWords )
{
int start = 0;
Font big = font.deriveFont( font.getSize() * 1.3f );
for ( Iterator it = boldWords.iterator(); it.hasNext(); )
{
String word = (String) it.next();
start = text.indexOf( word, start );
int end = start + word.length();
attrStr.addAttribute( TextAttribute.WEIGHT,
TextAttribute.WEIGHT_HEAVY, start, end );
attrStr.addAttribute( TextAttribute.FONT, big, start, end );
}
}
public void paint ( Graphics gr )
{
Graphics2D g = (Graphics2D) gr;
// Normalize the graphics context so that 1 point is exactly
// 1/72 inch and thus fonts will display at the correct sizes:
GraphicsConfiguration gc = g.getDeviceConfiguration();
g.transform( gc.getNormalizingTransform() );
// Compute the width, respecting the margins, and adding a border:
Insets insets = getInsets();
int border = 15;
int width = getSize().width - insets.right - insets.left
- 2 * border;
// Set the starting position to draw so that the (0,0) point
// is the top left pixel visible in the content pane:
g.translate( insets.left + border, insets.top + border );
int x = 0;
int y = 0;
// Get iterator for string:
AttributedCharacterIterator characterIterator =
attrStr.getIterator();
// Get font context from graphics:
FontRenderContext fontRenderContext = g.getFontRenderContext();
// Create measurer:
LineBreakMeasurer measurer = new LineBreakMeasurer(
characterIterator, fontRenderContext );
while ( measurer.getPosition() < characterIterator.getEndIndex() )
{
// Get line:
TextLayout textLayout = measurer.nextLayout( width );
// Move down to baseline:
y += textLayout.getAscent();
// Draw line:
textLayout.draw( g, x, y );
// Move down to top of next line:
y += textLayout.getDescent() + textLayout.getLeading();
}
}
// This method centers a JFrame on the screen:
private void centerWin ()
{
Toolkit tk = Toolkit.getDefaultToolkit();
int screenWidth = tk.getScreenSize().width;
int screenHeight = tk.getScreenSize().height;
int frameWidth = getSize().width;
int frameHeight = getSize().height;
setLocation( ( screenWidth - frameWidth ) / 2,
( screenHeight - frameHeight ) / 2 );
}
}