Home | Articles

Styled Text Tutorial
Lesson 2: Drawing Styled Text

[Contents] [Next]

This lesson explains how to draw simple and complex styled text using the java.awt.font.TextLayout class to create interesting and pleasing visual effects.

Draw a Line of Text

The java.awt.font.TextLayout class lets you create styled text from combinations of strings, fonts, and attribute sets. Once created, a TextLayout object cannot be edited, but its methods give you access to layout, font, caret, selection, and hit test information.

The following code uses Font, TextLayout, and FontRenderContext objects to draw a simple text string in 24 point Times Bold. Here is the complete TimesB.java source code.

24 pt Times Bold text
FontRenderContext frc = g2.getFontRenderContext();
Font f = new Font("Helvetica",Font.BOLD, 24);
String s = new String("24 Point Helvetica Bold");
TextLayout tl = new TextLayout(s, f, frc);
Dimension theSize=getSize();
g2.setColor(Color.green);
tl.draw(g2, theSize.width/30, theSize.height/2);

Draw Multiple Lines of Text

You can use the TextLayout and java.awt.LineBreakMeasurer classes to draw a paragraph of styled text. This next example uses a LineBreakMeasurer object to create and draw lines of text (text layouts) that fit within the component's width. Information about the font returned by the TextLayout getAscent and getDescent methods is used to position the lines in the component. The text is stored as an AttributedCharacterIterator so the font and point size attributes can be stored with the text.

Here is the complete source code for the LineBreakSample.java example. The SampleUtils.java class contains the text displayed by the LineBreakSample application.

Note: The LineBreakSample application supports foreign language text. See Lesson 4: Foreign Language Support for more information.
line break
public class LineBreakSample extends Component {

  private LineBreakMeasurer lineMeasurer;
  private int paragraphStart;
  private int paragraphEnd;

  public LineBreakSample
      (AttributedCharacterIterator paragraph) {

    FontRenderContext frc = 
      SampleUtils.getDefaultFontRenderContext();
    paragraphStart = paragraph.getBeginIndex();
    paragraphEnd = paragraph.getEndIndex();
    lineMeasurer = 
      new LineBreakMeasurer(paragraph, frc);
  }

  public void paint(Graphics g) {

    Graphics2D graphics2D = (Graphics2D) g;
    Dimension size = getSize();
    float formatWidth = (float) size.width;

    float drawPosY = 0;

    lineMeasurer.setPosition(paragraphStart);
    while (lineMeasurer.getPosition() < paragraphEnd) {

       TextLayout layout = 
		lineMeasurer.nextLayout(formatWidth);
       drawPosY += layout.getAscent();
       float drawPosX;
       if (layout.isLeftToRight()) {
        drawPosX = 0;
       }
       else {
         drawPosX = formatWidth - layout.getAdvance();
       }

       layout.draw(graphics2D, drawPosX, drawPosY);
       drawPosY += layout.getDescent() + 
				layout.getLeading();
     }
  }

A New Slant on Text

You can create a Font object from a transform object that provides information on how to rotate, scale, move, or shear the font. This next example uses a shear transformation to draw slanted text. First, the string is drawn without the transform, and then it is drawn in a different location with it. Here is the complete StillLife.java source code.

still life
  int w = getSize().width;
  int h = getSize().height;

//Create transforms
  AffineTransform at = new AffineTransform();
  at.setToTranslation(30, 50);
  AffineTransform fontAT = new AffineTransform();
  fontAT.shear(0.2, 0.0);

//Create fonts and text layouts
  FontRenderContext frc = g2.getFontRenderContext();
  Font theFont = new Font("Times", Font.BOLD, w/25);
  String s = new String("Still Life with Text");
  TextLayout tstring = new TextLayout(s, theFont, frc);

  Font theDerivedFont = theFont.deriveFont(fontAT);
  String str = new String("Still Life with Slanted Text");
  TextLayout tstring2 = new TextLayout(str, 
			theDerivedFont, frc);

//Draw regular string
  g2.setColor(Color.blue);
  g2.transform(at);
  tstring.draw(g2, (float)0, (float)0);

//Draw string with shear transformation on the font
  g2.setColor(Color.green);
  g2.transform(at);
  tstring2.draw(g2, (float)0, (float)0);

Filling a Clipping Area with an Image

This next example creates a clipping area from the text string The Starry Night, and uses Vincent van Gogh's painting, The Starry Night, to fill the clipping area. Here is the complete Starry.java source code.

The Starry Night by Vincent van Gogh The example code creates a text layout with the string The Starry Night in Times Bold. Next, it gets the width of the TextLayout bounds. The bounds contains all the pixels the layout can draw.

Then, it gets the outline of the text layout and uses the width of the bounds to calculate the X and Y positions for the origins of the layout. The outline is used to create a Shape object, and a Rectangle object is created from the bounds of the Shape object.

At this point, the graphics context color is set to blue and the Shape object is drawn into the graphics context. The image and code segment below show what has happened so far.

starry shape
FontRenderContext frc = g2.getFontRenderContext();
Font f = new Font("Times",Font.BOLD,w/10);
String s = new String("The Starry Night");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2, h/4);
Shape shape = tl.getOutline(transform);
Rectangle r = shape.getBounds();
g2.setColor(Color.blue);
g2.draw(shape);

Lastly, a clipping area is set on the graphics context using the Shape object, and the starry.gif image is drawn into the clipping area starting at the lower-left corner of the Rectangle object (r.x and r.y) and filling the full height and width of the Rectangle object.

The Starry Night
pattern in text
g2.setClip(shape);
g2.drawImage(img, r.x, r.y, r.width, r.height, this);

Filling a Clipping Area with Lines

For a slightly different effect, this next example creates a clipping area from the text layout By, fills the clipping area with blue, and draws yellow lines across the clipping area over the blue fill. Information returned by the TextLayout getAscent method is used to position the line on the display.

Here is the complete Clipping.java source code.

clipping
FontRenderContext frc = g2.getFontRenderContext();
Font f = new Font("Helvetica",Font.BOLD,w/8);
String s = new String("By");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2,h/2);
Shape shape = tl.getOutline(transform);
g2.setClip(shape);
g2.setColor(Color.blue);
g2.fill(shape.getBounds());
g2.setColor(Color.yellow);
for (int j = shape.getBounds().y; 
    j < shape.getBounds().y + 
    shape.getBounds ().height; j=j+3) {
  Line2D line = new Line2D.Float( 0.0f, (float) j, 
			(float) w, (float) j);
  g2.draw(line);
}

Filling a Clipping Area with Characters

This next example creates a clipping area from the text layout Vincent van Gogh, fills it with blue, and draws white asterisks (*) into the clipping area over the blue fill to give the effect of white stars in a blue sky. Here is the complete TextClipping.java source code.

Vincent Van Gogh
text with blue fill and white asterisks
FontRenderContext frc = g2.getFontRenderContext();
Font f = 
  new Font("Times New Roman Bold",Font.PLAIN,w/8);
String s = new String("Vincent van Gogh");
TextLayout tl = new TextLayout(s, f, frc);
float sw = (float) tl.getBounds().getWidth();
AffineTransform transform = new AffineTransform();
transform.setToTranslation(w/2-sw/2,h-h/6);
Shape shape = tl.getOutline(transform);
g2.setClip(shape);
g2.setColor(Color.blue);
g2.fill(shape.getBounds());
g2.setColor(Color.white);
f = new Font("Helvetica",Font.BOLD,10);
tl = new TextLayout("+", f, frc);
sw = (float) tl.getBounds().getWidth();

Rectangle r = shape.getBounds();
int x = r.x;
int y = r.y;
while ( y < (r.y + r.height+(int) tl.getAscent()) ) {
      tl.draw(g2, x, y);
      if ((x += (int) sw) > (r.x+r.width)) {
      x = r.x;
      y += (int) tl.getAscent();
      }
}

Text Attributes and Replacement Graphics

The TextAttribute class defines text attributes so you can define an Attributed string with text attributes. This next example uses text attributes to define the font and a character replacement image for the java.awt.text.AttributedString that contains the text The Starry Night. An ImageGraphicAttribute is created from an image to do this. Here is the complete MakeImage.java source code.

character replace
public void paint(Graphics g) {
  Graphics2D g2;
  g2 = (Graphics2D) g;
  g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
  g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

  Font f = new Font("Times",Font.BOLD, 24);
  AttributedString ats = 
    new AttributedString("The  Starry  Night");
  ImageGraphicAttribute iga = 
    new ImageGraphicAttribute(img, 
    (int) BOTTOM_ALIGNMENT);
  ats.addAttribute(
    TextAttribute.CHAR_REPLACEMENT, iga, 4, 5);
  ats.addAttribute(
    TextAttribute.CHAR_REPLACEMENT, iga, 11, 12);
  ats.addAttribute(TextAttribute.FONT, f, 0, 18);
  AttributedCharacterIterator iter = ats.getIterator();

  FontRenderContext frc = g2.getFontRenderContext();
  tl = new TextLayout(iter, frc);
  g2.setColor(Color.red);
  tl.draw(g2, (float)20, (float)30);
}

© 1994-2005 Sun Microsystems, Inc.