View screen snapshot

Download this source file

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

 


Send comments and questions to pollock@acm.org.
Valid HTML 4.01!   Valid CSS!   CAST: Bobby WorldWide Approved 508   CAST: Bobby WorldWide Approved AAA