Java/2D Graphics GUI/Text Layout

Материал из Java эксперт
Перейти к: навигация, поиск

A display of text, formatted by us instead of by AWT/Swing

     
/*
 * Copyright (c) Ian F. Darwin, http://www.darwinsys.ru/, 1996-2002.
 * All rights reserved. Software written by Ian F. Darwin and others.
 * $Id: LICENSE,v 1.8 2004/02/09 03:33:38 ian Exp $
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS""
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * 
 * Java, the Duke mascot, and all variants of Sun"s Java "steaming coffee
 * cup" logo are trademarks of Sun Microsystems. Sun"s, and James Gosling"s,
 * pioneering role in inventing and promulgating (and standardizing) the Java 
 * language and environment is gratefully acknowledged.
 * 
 * The pioneering role of Dennis Ritchie and Bjarne Stroustrup, of AT&T, for
 * inventing predecessor languages C and C++ is also gratefully acknowledged.
 */
import java.awt.ruponent;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.JFrame;
/**
 * A display of text, formatted by us instead of by AWT/Swing.
 * <P>
 * This program is distributed under the terms of the accompanying COPYRIGHT.txt
 * file (which is NOT the GNU General Public License). Please read it. Your use
 * of the software constitutes acceptance of the terms in the COPYRIGHT.txt
 * file.
 * 
 * @author Ian F. Darwin
 * @version $Id: TextFormat.java,v 1.3 2004/03/26 03:34:30 ian Exp $
 */
public class TextFormat extends Component {
  /** The text of this line */
  protected String text;
  /** The Font */
  protected Font font;
  /** The TextLayouts corresponding to "text" */
  List layouts;
  public Font getFont() {
    return font;
  }
  public void setFont(Font f) {
    font = f;
  }
  public String getText() {
    return text;
  }
  public void setText(String t) {
    text = t;
  }
  public void paint(Graphics g) {
    if (text == null || text.length() == 0)
      return;
    if (layouts == null)
      getLayouts(g);
    Point pen = new Point(0, 0);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(java.awt.Color.black); // or a property
    g2d.setFont(font);
    Iterator it = layouts.iterator();
    while (it.hasNext()) {
      TextLayout layout = (TextLayout) it.next();
      pen.y += (layout.getAscent());
      g2d.setFont(font);
      layout.draw(g2d, pen.x, pen.y);
      pen.y += layout.getDescent();
      //pen.y += leading;
    }
  }
  /**
   * Lazy evaluation of the List of TextLayout objects corresponding to this
   * MText. Some things are approximations!
   */
  private void getLayouts(Graphics g) {
    layouts = new ArrayList();
    Point pen = new Point(10, 20);
    Graphics2D g2d = (Graphics2D) g;
    FontRenderContext frc = g2d.getFontRenderContext();
    AttributedString attrStr = new AttributedString(text);
    attrStr.addAttribute(TextAttribute.FONT, font, 0, text.length());
    LineBreakMeasurer measurer = new LineBreakMeasurer(attrStr
        .getIterator(), frc);
    float wrappingWidth;
    wrappingWidth = getSize().width - 15;
    while (measurer.getPosition() < text.length()) {
      TextLayout layout = measurer.nextLayout(wrappingWidth);
      layouts.add(layout);
    }
  }
  public static void main(String[] args) {
    JFrame jf = new JFrame("Demo");
    Container cp = jf.getContentPane();
    TextFormat tl = new TextFormat();
    tl.setFont(new Font("SansSerif", Font.BOLD, 42));
    tl.setText("The quick brown fox jumped over the lazy cow");
    cp.add(tl);
    jf.setSize(300, 200);
    jf.setVisible(true);
  }
}



Another Line Break Demo

     
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
  String text = "this is a test. This is a test. This is a test. This is a test";
  AttributedString attribString = new AttributedString(text);
  AttributedCharacterIterator attribCharIterator;
  public Main() {
    setSize(350, 400);    
    attribString.addAttribute(TextAttribute.FOREGROUND, Color.blue, 0, text
        .length());
    attribString.addAttribute(TextAttribute.FONT, new Font("sanserif", Font.ITALIC, 20), 
        0, text.length());
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    attribCharIterator = attribString.getIterator();
    FontRenderContext frc = new FontRenderContext(null, false, false);
    LineBreakMeasurer lbm = new LineBreakMeasurer(attribCharIterator, frc);
    int x = 10, y = 20; 
    int w = getWidth(); 
    float wrappingWidth = w - 15;
    while (lbm.getPosition() < text.length()) {
      TextLayout layout = lbm.nextLayout(wrappingWidth);
      y += layout.getAscent();
      layout.draw(g2, x, y);
      y += layout.getDescent() + layout.getLeading();
    }
  }
  public static void main(String arg[]) {
    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add("Center", new Main());
    frame.pack();
    frame.setSize(new Dimension(350, 400));
    frame.setVisible(true);
  }
}



Caret action

     
import java.awt.BasicStroke;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class TextLayoutWithCarets extends JLabel {
  private TextHitInfo mHit;
  private TextLayout mLayout;
  private boolean mInitialized = false;
  public TextLayoutWithCarets() {
    addKeyListener(new KeyListener() {
      public void keyPressed(KeyEvent ke) {
        if (ke.getKeyCode() == KeyEvent.VK_RIGHT) {
          mHit = mLayout.getNextRightHit(mHit.getInsertionIndex());
          if (mHit == null)
            mHit = mLayout.getNextLeftHit(1);
          repaint();
        } else if (ke.getKeyCode() == KeyEvent.VK_LEFT) {
          mHit = mLayout.getNextLeftHit(mHit.getInsertionIndex());
          if (mHit == null)
            mHit = mLayout.getNextRightHit(mLayout
                .getCharacterCount() - 1);
          repaint();
        }
      }
      public void keyTyped(KeyEvent arg0) {
        // TODO Auto-generated method stub
        
      }
      public void keyReleased(KeyEvent arg0) {
        // TODO Auto-generated method stub
        
      }
    });
  }
  private void initialize(Graphics2D g2) {
    String s = "Java Source and Support.";
    // Create a plain and italic font.
    int fontSize = 32;
    Font font = new Font("Lucida Sans Regular", Font.PLAIN, fontSize);
    Font italicFont = new Font("Lucida Sans Oblique", Font.ITALIC, fontSize);
    // Create an Attributed String
    AttributedString as = new AttributedString(s);
    as.addAttribute(TextAttribute.FONT, font);
    as.addAttribute(TextAttribute.FONT, italicFont, 2, 5);
    // Get the iterator.
    AttributedCharacterIterator iterator = as.getIterator();
    // Create a TextLayout.
    FontRenderContext frc = g2.getFontRenderContext();
    mLayout = new TextLayout(iterator, frc);
    mHit = mLayout.getNextLeftHit(1);
    
    // Respond to left and right arrow keys.
    mInitialized = true;
  }
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
        RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    if (mInitialized == false)
      initialize(g2);
    float x = 20, y = 80;
    mLayout.draw(g2, x, y);
    // Create a plain stroke and a dashed stroke.
    Stroke[] caretStrokes = new Stroke[2];
    caretStrokes[0] = new BasicStroke();
    caretStrokes[1] = new BasicStroke(1, BasicStroke.CAP_BUTT,
        BasicStroke.JOIN_ROUND, 0, new float[] { 4, 4 }, 0);
    // Now draw the carets
    Shape[] carets = mLayout.getCaretShapes(mHit.getInsertionIndex());
    for (int i = 0; i < carets.length; i++) {
      if (carets[i] != null) {
        AffineTransform at = AffineTransform.getTranslateInstance(x, y);
        Shape shape = at.createTransformedShape(carets[i]);
        g2.setStroke(caretStrokes[i]);
        g2.draw(shape);
      }
    }
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new TextLayoutWithCarets());
    f.setSize(350, 250);
    f.setVisible(true);
  }
}



Caret and TextLayout

     
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TextLayoutPanel extends JPanel {
  TextLayout layout;
  FontRenderContext frc;
  Font font;
  Rectangle2D rect;
  float rx, ry, rw, rh;
  TextHitInfo hitInfo;
  Color caretColor;
  int hit1, hit2;
  int w, h;
  public TextLayoutPanel() {
    setBackground(Color.white);
    setForeground(Color.black);
    setSize(400, 200);
    addMouseListener(new MouseHandler());
    addMouseMotionListener(new MouseMotionHandler());
    w = getWidth();
    h = getHeight();
    String text = "Java Source and Support";
    font = new Font("Arial", Font.PLAIN, 36);
    frc = new FontRenderContext(null, false, false);
    layout = new TextLayout(text, font, frc);
    rx = (float) (w / 2 - layout.getBounds().getWidth() / 2);
    ry = (float) 3 * h / 4;
    rw = (float) (layout.getBounds().getWidth());
    rh = (float) (layout.getBounds().getHeight());
    rect = new Rectangle2D.Float(rx, ry, rw, rh);
    caretColor = getForeground();
  }
  public void update(Graphics g) {
    g.clearRect(0, 0, getWidth(), getHeight());
    paintComponent(g);
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2 = (Graphics2D) g;
    AffineTransform at = AffineTransform.getTranslateInstance(rx, ry);
    Shape hilight = layout.getLogicalHighlightShape(hit1, hit2);
    hilight = at.createTransformedShape(hilight);
    g2.setColor(Color.lightGray);
    g2.fill(hilight);
    g2.setColor(Color.black);
    layout.draw(g2, rx, ry);
    // Draw caret
    Shape[] caretShapes = layout.getCaretShapes(hit1);
    Shape caret = at.createTransformedShape(caretShapes[0]);
    g2.setColor(caretColor);
    g2.draw(caret);
  }
  public int getHitLocation(int mouseX, int mouseY) {
    hitInfo = layout.hitTestChar(mouseX, mouseY, rect);
    return hitInfo.getInsertionIndex();
  }
  class MouseHandler extends MouseAdapter {
    public void mouseClicked(MouseEvent e) {
      caretColor = getForeground();
      hit1 = getHitLocation(e.getX(), e.getY());
      hit2 = hit1;
      repaint();
    }
    public void mousePressed(MouseEvent e) {
      caretColor = getForeground();
      hit1 = getHitLocation(e.getX(), e.getY());
      hit2 = hit1;
      repaint();
    }
    public void mouseReleased(MouseEvent e) {
      hit2 = getHitLocation(e.getX(), e.getY());
      repaint();
    }
  }
  class MouseMotionHandler extends MouseMotionAdapter {
    public void mouseDragged(MouseEvent e) {
      caretColor = getBackground();
      hit2 = getHitLocation(e.getX(), e.getY());
      repaint();
    }
  }
  public static void main(String arg[]) {
    JFrame frame = new JFrame();
    frame.setBackground(Color.white);
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.getContentPane().add("Center", new TextLayoutPanel());
    frame.pack();
    frame.setSize(new Dimension(400, 300));
    frame.setVisible(true);
  }
}



Drawing a Paragraph of Text

    
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    drawParagraph(g2d, "adfasdfaf", 20);
  }
  void drawParagraph(Graphics2D g, String paragraph, float width) {
    LineBreakMeasurer linebreaker = new LineBreakMeasurer(new AttributedString(paragraph)
        .getIterator(), g.getFontRenderContext());
    int y = 0;
    while (linebreaker.getPosition() < paragraph.length()) {
      TextLayout textLayout = linebreaker.nextLayout(width);
      y += textLayout.getAscent();
      textLayout.draw(g, 0, y);
      y += textLayout.getDescent() + textLayout.getLeading();
    }
  }
  public static void main(String[] args) {
    JFrame frame = new JFrame("Basic Shapes");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new Main());
    frame.setSize(350, 250);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Drawing multi-line text with AttributedString and LineBreakMeasurer

    
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.UIManager;
public class Main extends JFrame {
  public void paint(Graphics g) {
    String input = "this is a test.this is a test.this is a test.this is a test.";
    AttributedString attributedString = new AttributedString(input);
    attributedString.addAttribute(TextAttribute.FONT, (Font) UIManager
        .get("Label.font"));
    Color color = (Color) UIManager.get("Label.foreground");
    attributedString.addAttribute(TextAttribute.FOREGROUND, color);
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    int width = getSize().width;
    int x = 10;
    int y = 30;
    AttributedCharacterIterator characterIterator = attributedString
        .getIterator();
    FontRenderContext fontRenderContext = g2d.getFontRenderContext();
    LineBreakMeasurer measurer = new LineBreakMeasurer(characterIterator,
        fontRenderContext);
    while (measurer.getPosition() < characterIterator.getEndIndex()) {
      TextLayout textLayout = measurer.nextLayout(width);
      y += textLayout.getAscent();
      textLayout.draw(g2d, x, y);
      y += textLayout.getDescent() + textLayout.getLeading();
    }
  }
  public static void main(String args[]) {
    JFrame frame = new Main();
    frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
    frame.setSize(20, 200);
    frame.setVisible(true);
  }
}



Drawing Text with Mixed Styles

    
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    AttributedString astr = new AttributedString("aString");
    astr.addAttribute(TextAttribute.FONT, new Font("", 1, 30), 1, 2);
    astr.addAttribute(TextAttribute.BACKGROUND, Color.red, 2, 3);
    TextLayout tl = new TextLayout(astr.getIterator(), g2d.getFontRenderContext());
    tl.draw(g2d, 10, 20);
  }
  public static void main(String[] args) {
    JFrame frame = new JFrame("Basic Shapes");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new Main());
    frame.setSize(350, 250);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Draws the string at the specified location underlining the specified character

    
/*
 * Copyright (c) 2001-2009 JGoodies Karsten Lentzsch. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of JGoodies Karsten Lentzsch nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import java.awt.*;
import java.awt.print.PrinterGraphics;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.plaf.basic.BasicGraphicsUtils;

/**
 * Provides convenience behavior used by the JGoodies Looks.<p>
 *
 * <strong>Note:</strong> This class is not part of the public Looks API.
 * It should be treated as library internal and should not be used by
 * API users. It may be removed or changed with the Looks version
 * without further notice.
 *
 * @author  Karsten Lentzsch
 * @author  Andrej Golovnin
 * @version $Revision: 1.12 $
 *
 * @since 2.0
 */
public final class RenderingUtils {
    private static final String PROP_DESKTOPHINTS = "awt.font.desktophints";
    private static final String SWING_UTILITIES2_NAME ="sun.swing.SwingUtilities2";
    /**
     * In Java 5 or later, this field holds the public static method
     * <code>SwingUtilities2#drawStringUnderlinedAt</code> that has been added
     * for Java 5.
     */
    private static Method drawStringUnderlineCharAtMethod = null;
    static {
   
            drawStringUnderlineCharAtMethod = getMethodDrawStringUnderlineCharAt();
       
    }

    private RenderingUtils() {
        // Overrides default constructor; prevents instantiation.
    }
    /**
     * Draws the string at the specified location underlining the specified
     * character.
     *
     * @param c JComponent that will display the string, may be null
     * @param g Graphics to draw the text to
     * @param text String to display
     * @param underlinedIndex Index of a character in the string to underline
     * @param x X coordinate to draw the text at
     * @param y Y coordinate to draw the text at
     */
    public static void drawStringUnderlineCharAt(JComponent c,Graphics g,
                           String text, int underlinedIndex, int x,int y) {
      
            if (drawStringUnderlineCharAtMethod != null) {
                try {
                    drawStringUnderlineCharAtMethod.invoke(null,
                            new Object[] {c, g, text, new Integer(underlinedIndex),
                                          new Integer(x), new Integer(y)});
                    return;
                } catch (IllegalArgumentException e) {
                    // Use the BasicGraphicsUtils as fallback
                } catch (IllegalAccessException e) {
                    // Use the BasicGraphicsUtils as fallback
                } catch (InvocationTargetException e) {
                    // Use the BasicGraphicsUtils as fallback
                }
            }
            Graphics2D g2 = (Graphics2D) g;
            Map oldRenderingHints = installDesktopHints(g2);
            BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, underlinedIndex, x, y);
            if (oldRenderingHints != null) {
                g2.addRenderingHints(oldRenderingHints);
            }
            return;
      
        BasicGraphicsUtils.drawStringUnderlineCharAt(g, text, underlinedIndex, x, y);
    }

    // Private Helper Code ****************************************************

    private static Method getMethodDrawStringUnderlineCharAt() {
        try {
            Class clazz = Class.forName(SWING_UTILITIES2_NAME);
            return clazz.getMethod(
                    "drawStringUnderlineCharAt",
                    new Class[] {JComponent.class, Graphics.class, String.class, Integer.TYPE, Integer.TYPE, Integer.TYPE}
                    );
        } catch (ClassNotFoundException e) {
            // returns null
        } catch (SecurityException e) {
            // returns null
        } catch (NoSuchMethodException e) {
            // returns null
        }
        return null;
    }

    private static Map installDesktopHints(Graphics2D g2) {
        Map oldRenderingHints = null;
 
            Map desktopHints = desktopHints(g2);
            if (desktopHints != null && !desktopHints.isEmpty()) {
                oldRenderingHints = new HashMap(desktopHints.size());
                RenderingHints.Key key;
                for (Iterator i = desktopHints.keySet().iterator(); i.hasNext();) {
                    key = (RenderingHints.Key) i.next();
                    oldRenderingHints.put(key, g2.getRenderingHint(key));
                }
                g2.addRenderingHints(desktopHints);
            }
    
        return oldRenderingHints;
    }

    private static Map desktopHints(Graphics2D g2) {
        if (isPrinting(g2)) {
            return null;
        }
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        GraphicsDevice device = g2.getDeviceConfiguration().getDevice();
        Map desktopHints = (Map) toolkit.getDesktopProperty(
                PROP_DESKTOPHINTS + "." + device.getIDstring());
        if (desktopHints == null) {
            desktopHints = (Map) toolkit.getDesktopProperty(PROP_DESKTOPHINTS);
        }
        // It is possible to get a non-empty map but with disabled AA.
        if (desktopHints != null) {
            Object aaHint = desktopHints.get(RenderingHints.KEY_TEXT_ANTIALIASING);
            if ((aaHint == RenderingHints.VALUE_TEXT_ANTIALIAS_OFF) ||
                (aaHint == RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT)) {
                desktopHints = null;
            }
        }
        return desktopHints;
    }

    private static boolean isPrinting(Graphics g) {
        return g instanceof PrintGraphics || g instanceof PrinterGraphics;
    }

}



Draw text with TextLayout

     
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
  public Main() {
    setBackground(Color.white);
  }
  public void paint(Graphics g) {
    Graphics2D g2D;
    g2D = (Graphics2D) g;
    g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    g2D.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    FontRenderContext frc = g2D.getFontRenderContext();
    Font font1 = new Font("Courier", Font.BOLD, 24);
    String str1 = new String("Java");
    TextLayout tl = new TextLayout(str1, font1, frc);
    g2D.setColor(Color.gray);
    tl.draw(g2D, 50, 150);
  }
  public static void main(String s[]) {
    JFrame frame1 = new JFrame("2D Text");
    frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame1.getContentPane().add("Center", new Main());
    frame1.pack();
    frame1.setSize(new Dimension(500, 300));
    frame1.setVisible(true);
  }
}



Getting the Shape from the Outline of Text

    
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.font.FontRenderContext;
import java.awt.font.TextLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    g2d.draw(getTextShape(g2d, "asdf", new Font("", 1, 30)));
  }
  public Shape getTextShape(Graphics2D g2d, String str, Font font) {
    FontRenderContext frc = g2d.getFontRenderContext();
    TextLayout tl = new TextLayout(str, font, frc);
    return tl.getOutline(null);
  }
  public static void main(String[] args) {
    JFrame frame = new JFrame("Basic Shapes");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.add(new Main());
    frame.setSize(350, 250);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}



Hightlight text by drag and selection

     
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.font.FontRenderContext;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Highlights extends JPanel {
  private TextLayout textLayout;
  private TextHitInfo firstHit, secondHit;
  private int x = 40, y = 80;
    public Highlights(){
    addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent me) {
        firstHit = textLayout.hitTestChar(me.getX() - x, me.getY()
            - y);
        secondHit = null;
      }
      public void mouseReleased(MouseEvent me) {
        secondHit = textLayout.hitTestChar(me.getX() - x, me.getY()
            - y);
        repaint();
      }
    });
    addMouseMotionListener(new MouseMotionAdapter() {
      public void mouseDragged(MouseEvent me) {
        secondHit = textLayout.hitTestChar(me.getX() - x, me.getY()
            - y);
        repaint();
      }
    });
        
    }
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
        RenderingHints.VALUE_FRACTIONALMETRICS_ON);
    String s = "Drag the text to highlight Java Source and Support.";
    Font font = new Font("Serif", Font.PLAIN, 32);
    if (textLayout == null) {
      FontRenderContext frc = g2.getFontRenderContext();
      textLayout = new TextLayout(s, font, frc);
    }
    // Draw the highlight.
    if (firstHit != null && secondHit != null) {
      Shape base = textLayout.getLogicalHighlightShape(firstHit
          .getInsertionIndex(), secondHit.getInsertionIndex());
      AffineTransform at = AffineTransform.getTranslateInstance(x, y);
      Shape highlight = at.createTransformedShape(base);
      g2.setPaint(Color.white);
      g2.fill(highlight);
    }
    g2.setPaint(Color.black);
    textLayout.draw(g2, x, y);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new Highlights());
    f.setSize(850, 250);
    f.show();
  }
}



JFreeChart: Draw String Demo

     
/* ========================================================================
 * JCommon : a free general purpose class library for the Java(tm) platform
 * ========================================================================
 *
 * (C) Copyright 2000-2004, by Object Refinery Limited and Contributors.
 * 
 * Project Info:  http://www.jfree.org/jcommon/index.html
 *
 * This library is free software; you can redistribute it and/or modify it under the terms
 * of the GNU Lesser General Public License as published by the Free Software Foundation;
 * either version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 * 
 * -------------------
 * DrawStringDemo.java
 * -------------------
 * (C) Copyright 2003, 2004, by Object Refinery Limited.
 *
 * Original Author:  David Gilbert (for Object Refinery Limited);
 * Contributor(s):   -;
 *
 * $Id: DrawStringDemo.java,v 1.3 2005/06/01 14:12:28 taqua Exp $
 *
 * Changes
 * -------
 * 10-Jun-2003 : Version 1;
 *
 */
package org.jfree.demo;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.FontChooserPanel;
import org.jfree.ui.RefineryUtilities;
import org.jfree.ui.Spinner;
import org.jfree.ui.TextAnchor;
/**
 * A demo of some of the string drawing methods in the JCommon class library.
 *
 * @author David Gilbert
 */
public class DrawStringDemo extends ApplicationFrame 
                            implements ActionListener, PropertyChangeListener {
    /** The alignment anchor for the first panel. */
    private JComboBox combo1;
    /** The alignment anchor for the second panel. */
    private JComboBox combo2;
    /** The rotation anchor for the second panel. */
    private JComboBox combo3;
    /** A spinner for the second panel. */
    private Spinner spinner;
    /** String panel 1. */
    private DrawStringPanel drawStringPanel1;
    /** String panel 2. */
    private DrawStringPanel drawStringPanel2;
    /**
     * Creates a new demo instance.
     *
     * @param title  the frame title.
     */
    public DrawStringDemo(final String title) {
        super(title);
        setContentPane(createContentPane());
    }
    /**
     * Receives action events.
     *
     * @param event  the event.
     */
    public void actionPerformed(final ActionEvent event) {
        if (event.getActionCommand().equals("fontButton.clicked")) {
            displayFontDialog();
        }
        if (event.getActionCommand().equals("combo1.changed")) {
            handleCombo1Change();
        }
        if (event.getActionCommand().equals("combo2.changed")) {
            handleCombo2Change();
        }
        if (event.getActionCommand().equals("combo3.changed")) {
            handleCombo3Change();
        }
    }
    /**
     * Receives property change events.
     *
     * @param event  the event.
     */
    public void propertyChange(final PropertyChangeEvent event) {
        final int r = this.spinner.getValue();
        final double angle = Math.PI * 2.0 * (r / 360.0);
        this.drawStringPanel2.setAngle(angle);
        this.drawStringPanel2.invalidate();
        this.drawStringPanel2.repaint();
    }
    /**
     * Updates the display when combo 1 is updated.
     */
    private void handleCombo1Change() {
        final String text = this.rubo1.getSelectedItem().toString();
        this.drawStringPanel1.setAnchor(convertStringToAnchor(text));
        this.drawStringPanel1.invalidate();
        this.drawStringPanel1.repaint();
    }
    /**
     * Updates the display when combo 2 is updated.
     */
    private void handleCombo2Change() {
        final String text = this.rubo2.getSelectedItem().toString();
        this.drawStringPanel2.setAnchor(convertStringToAnchor(text));
        this.drawStringPanel2.invalidate();
        this.drawStringPanel2.repaint();
    }
    /**
     * Updates the display when combo 3 is updated.
     */
    private void handleCombo3Change() {
        final String text = this.rubo3.getSelectedItem().toString();
        this.drawStringPanel2.setRotationAnchor(convertStringToAnchor(text));
        this.drawStringPanel2.invalidate();
        this.drawStringPanel2.repaint();
    }
    /**
     * Creates the content pane for the demo frame.
     *
     * @return The content pane.
     */
    private JPanel createContentPane() {
        final JPanel content = new JPanel(new BorderLayout());
        final JTabbedPane tabs = new JTabbedPane();
        tabs.add("Alignment", createTab1Content());
        tabs.add("Rotation", createTab2Content());
        content.add(tabs);
        return content;
    }
    /**
     * Creates the content for tab 1.
     *
     * @return The content panel.
     */
    private JPanel createTab1Content() {
        final JPanel content = new JPanel(new BorderLayout());
        this.rubo1 = new JComboBox();
        this.rubo1.setActionCommand("combo1.changed");
        populateTextAnchorCombo(this.rubo1);
        this.rubo1.addActionListener(this);
        final JPanel controls = new JPanel();
        controls.add(this.rubo1);
        final JButton fontButton = new JButton("Select Font...");
        fontButton.setActionCommand("fontButton.clicked");
        fontButton.addActionListener(this);
        controls.add(fontButton);
        content.add(controls, BorderLayout.NORTH);
        this.drawStringPanel1 = new DrawStringPanel("0123456789", false);
        content.add(this.drawStringPanel1);
        return content;
    }
    /**
     * Creates the content for tab 2.
     *
     * @return The content panel.
     */
    private JPanel createTab2Content() {
        final JPanel content = new JPanel(new BorderLayout());
        final JPanel controls = new JPanel();
        controls.add(new JLabel("Text anchor: "));
        this.rubo2 = new JComboBox();
        populateTextAnchorCombo(this.rubo2);
        this.rubo2.setActionCommand("combo2.changed");
        this.rubo2.addActionListener(this);
        controls.add(this.rubo2);
        controls.add(new JLabel("Rotation anchor: "));
        this.rubo3 = new JComboBox();
        populateTextAnchorCombo(this.rubo3);
        this.rubo3.setActionCommand("combo3.changed");
        this.rubo3.addActionListener(this);
        controls.add(this.rubo3);
        this.spinner = new Spinner(0);
        this.spinner.addPropertyChangeListener(this);
        controls.add(this.spinner);
        content.add(controls, BorderLayout.NORTH);
        this.drawStringPanel2 = new DrawStringPanel("Rotated Text", true);
        content.add(this.drawStringPanel2);
        return content;
    }
    /**
     * Displays a primitive font chooser dialog to allow the user to change the font.
     */
    private void displayFontDialog() {
        final FontChooserPanel panel = new FontChooserPanel(this.drawStringPanel1.getFont());
        final int result = JOptionPane.showConfirmDialog(this, panel, "Font Selection",
                                                   JOptionPane.OK_CANCEL_OPTION,
                                                   JOptionPane.PLAIN_MESSAGE);
        if (result == JOptionPane.OK_OPTION) {
            this.drawStringPanel1.setFont(panel.getSelectedFont());
            this.drawStringPanel2.setFont(panel.getSelectedFont());
        }
    }
    /**
     * Populates a combo box with the available {@link TextAnchor} options.
     *
     * @param combo  the combo box.
     */
    private void populateTextAnchorCombo(final JComboBox combo) {
        combo.addItem("TextAnchor.TOP_LEFT");
        combo.addItem("TextAnchor.TOP_CENTER");
        combo.addItem("TextAnchor.TOP_RIGHT");
        combo.addItem("TextAnchor.HALF_ASCENT_LEFT");
        combo.addItem("TextAnchor.HALF_ASCENT_CENTER");
        combo.addItem("TextAnchor.HALF_ASCENT_RIGHT");
        combo.addItem("TextAnchor.CENTER_LEFT");
        combo.addItem("TextAnchor.CENTER");
        combo.addItem("TextAnchor.CENTER_RIGHT");
        combo.addItem("TextAnchor.BASELINE_LEFT");
        combo.addItem("TextAnchor.BASELINE_CENTER");
        combo.addItem("TextAnchor.BASELINE_RIGHT");
        combo.addItem("TextAnchor.BOTTOM_LEFT");
        combo.addItem("TextAnchor.BOTTOM_CENTER");
        combo.addItem("TextAnchor.BOTTOM_RIGHT");
    }
    /**
     * Converts a string to a corresponding {@link TextAnchor} instance.
     *
     * @param text  the text.
     *
     * @return The anchor.
     */
    private TextAnchor convertStringToAnchor(final String text) {
        if (text.equals("TextAnchor.TOP_LEFT")) {
            return TextAnchor.TOP_LEFT;
        }
        else if (text.equals("TextAnchor.TOP_CENTER")) {
            return TextAnchor.TOP_CENTER;
        }
        else if (text.equals("TextAnchor.TOP_RIGHT")) {
            return TextAnchor.TOP_RIGHT;
        }
        else if (text.equals("TextAnchor.CENTER_LEFT")) {
            return TextAnchor.CENTER_LEFT;
        }
        else if (text.equals("TextAnchor.CENTER")) {
            return TextAnchor.CENTER;
        }
        else if (text.equals("TextAnchor.CENTER_RIGHT")) {
            return TextAnchor.CENTER_RIGHT;
        }
        else if (text.equals("TextAnchor.HALF_ASCENT_LEFT")) {
            return TextAnchor.HALF_ASCENT_LEFT;
        }
        else if (text.equals("TextAnchor.HALF_ASCENT_CENTER")) {
            return TextAnchor.HALF_ASCENT_CENTER;
        }
        else if (text.equals("TextAnchor.HALF_ASCENT_RIGHT")) {
            return TextAnchor.HALF_ASCENT_RIGHT;
        }
        else if (text.equals("TextAnchor.BASELINE_LEFT")) {
            return TextAnchor.BASELINE_LEFT;
        }
        else if (text.equals("TextAnchor.BASELINE_CENTER")) {
            return TextAnchor.BASELINE_CENTER;
        }
        else if (text.equals("TextAnchor.BASELINE_RIGHT")) {
            return TextAnchor.BASELINE_RIGHT;
        }
        else if (text.equals("TextAnchor.BOTTOM_LEFT")) {
            return TextAnchor.BOTTOM_LEFT;
        }
        else if (text.equals("TextAnchor.BOTTOM_CENTER")) {
            return TextAnchor.BOTTOM_CENTER;
        }
        else if (text.equals("TextAnchor.BOTTOM_RIGHT")) {
            return TextAnchor.BOTTOM_RIGHT;
        }
        else {
            return null;
        } 
    }
    /**
     * The starting point for the demo.
     *
     * @param args  ignored.
     */
    public static void main(final String[] args) {
        final DrawStringDemo demo = new DrawStringDemo("DrawString Demo");
        demo.pack();
        RefineryUtilities.centerFrameOnScreen(demo);
        demo.setVisible(true);
    }
}



Line break for textlayout

     
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.Hashtable;
import javax.swing.JApplet;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LineBreakSample extends JPanel {
  private LineBreakMeasurer lineMeasurer;
  // the first character in the paragraph.
  private int paragraphStart;
  // the first character after the end of the paragraph.
  private int paragraphEnd;
  private static final Hashtable map = new Hashtable();
  static {
    map.put(TextAttribute.SIZE, new Float(18.0));
  }
  private static AttributedString vanGogh = new AttributedString(
      "Many people believe that Vincent van Gogh painted his best works "
          + "during the two-year period he spent in Provence. Here is where he "
          + "painted The Starry Night--which some consider to be his greatest "
          + "work of all. However, as his artistic brilliance reached new heights "
          + "in Provence, his physical and mental health plummeted. ",
      map);
  public LineBreakSample() {
    AttributedCharacterIterator paragraph = vanGogh.getIterator();
    paragraphStart = paragraph.getBeginIndex();
    paragraphEnd = paragraph.getEndIndex();
    // Create a new LineBreakMeasurer from the paragraph.
    lineMeasurer = new LineBreakMeasurer(paragraph,
        new FontRenderContext(null, false, false));
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    setBackground(Color.white);
    Graphics2D graphics2D = (Graphics2D) g;
    // Set formatting width to width of Component.
    Dimension size = getSize();
    float formatWidth = (float) size.width;
    float drawPosY = 0;
    lineMeasurer.setPosition(paragraphStart);
    // Get lines from lineMeasurer until the entire
    // paragraph has been displayed.
    while (lineMeasurer.getPosition() < paragraphEnd) {
      // Retrieve next layout.
      TextLayout layout = lineMeasurer.nextLayout(formatWidth);
      // Move y-coordinate by the ascent of the layout.
      drawPosY += layout.getAscent();
      // Compute pen x position. If the paragraph is
      // right-to-left, we want to align the TextLayouts
      // to the right edge of the panel.
      float drawPosX;
      if (layout.isLeftToRight()) {
        drawPosX = 0;
      } else {
        drawPosX = formatWidth - layout.getAdvance();
      }
      // Draw the TextLayout at (drawPosX, drawPosY).
      layout.draw(graphics2D, drawPosX, drawPosY);
      // Move y-coordinate in preparation for next layout.
      drawPosY += layout.getDescent() + layout.getLeading();
    }
  }
  public static void main(String[] args) {
    JFrame f = new JFrame("");
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    LineBreakSample controller = new LineBreakSample();
    f.getContentPane().add(controller,"Center");
    f.setSize(new Dimension(400, 250));
    f.setVisible(true);
  }
}



LineMetrics: the metrics to layout characters along a line

     
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Line2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class LineMetricsIllustration extends JPanel{
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    Font font = new Font("Serif", Font.PLAIN, 72);
    g2.setFont(font);
    String s = "Java Source and Support";
    float x = 50, y = 150;
    // Draw the baseline.
    FontRenderContext frc = g2.getFontRenderContext();
    float width = (float) font.getStringBounds(s, frc).getWidth();
    Line2D baseline = new Line2D.Float(x, y, x + width, y);
    g2.setPaint(Color.red);
    g2.draw(baseline);
    // Draw the ascent.
    LineMetrics lm = font.getLineMetrics(s, frc);
    Line2D ascent = new Line2D.Float(x, y - lm.getAscent(), x + width, y
        - lm.getAscent());
    g2.draw(ascent);
    // Draw the descent.
    Line2D descent = new Line2D.Float(x, y + lm.getDescent(), x + width, y
        + lm.getDescent());
    g2.draw(descent);
    // Draw the leading.
    Line2D leading = new Line2D.Float(x, y + lm.getDescent()
        + lm.getLeading(), x + width, y + lm.getDescent()
        + lm.getLeading());
    g2.draw(leading);
    // Render the string.
    g2.setPaint(Color.black);
    g2.drawString(s, x, y);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new LineMetricsIllustration());
    f.setSize(850, 250);
    f.show();
    
  }
}



Mouse hit and textlayout

     
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextAttribute;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import java.awt.geom.Point2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.Hashtable;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class HitTestSample extends JPanel {
  private static final Color STRONG_CARET_COLOR = Color.red;
  private static final Color WEAK_CARET_COLOR = Color.black;
  private TextLayout textLayout;
  private int insertionIndex;
  private static final FontRenderContext DEFAULT_FRC = new FontRenderContext(
      null, false, false);
  private static final Hashtable map = new Hashtable();
  static {
    map.put(TextAttribute.SIZE, new Float(18.0));
  }
  private static AttributedString helloWorld = new AttributedString(
      "Java Source and Support.", map);
  public HitTestSample() {
    AttributedCharacterIterator text = helloWorld.getIterator();
    // Create a new TextLayout from the given text.
    textLayout = new TextLayout(text, DEFAULT_FRC);
    // Initilize insertionIndex.
    insertionIndex = 0;
    addMouseListener(new HitTestMouseListener());
  }
  public Dimension getPreferredSize() {
    return new Dimension(400, 250);
  }
  private Point2D computeLayoutOrigin() {
    Dimension size = getPreferredSize();
    Point2D.Float origin = new Point2D.Float();
    origin.x = (float) (size.width - textLayout.getAdvance()) / 2;
    origin.y = (float) (size.height - textLayout.getDescent() + textLayout
        .getAscent()) / 2;
    return origin;
  }
  public void paintComponent(Graphics g) {
    super.paintComponent(g);
    setBackground(Color.white);
    Graphics2D graphics2D = (Graphics2D) g;
    Point2D origin = computeLayoutOrigin();
    graphics2D.translate(origin.getX(), origin.getY());
    // Draw textLayout.
    textLayout.draw(graphics2D, 0, 0);
    // Retrieve caret Shapes for insertionIndex.
    Shape[] carets = textLayout.getCaretShapes(insertionIndex);
    graphics2D.setColor(STRONG_CARET_COLOR);
    graphics2D.draw(carets[0]);
    if (carets[1] != null) {
      graphics2D.setColor(WEAK_CARET_COLOR);
      graphics2D.draw(carets[1]);
    }
  }
  private class HitTestMouseListener extends MouseAdapter {
    public void mouseClicked(MouseEvent e) {
      Point2D origin = computeLayoutOrigin();
      // Compute the mouse click location relative to
      // textLayout"s origin.
      float clickX = (float) (e.getX() - origin.getX());
      float clickY = (float) (e.getY() - origin.getY());
      // Get the character position of the mouse click.
      TextHitInfo currentHit = textLayout.hitTestChar(clickX, clickY);
      insertionIndex = currentHit.getInsertionIndex();
      // Repaint the Component so the new caret(s) will be displayed.
      repaint();
    }
  }
  public static void main(String[] args) {
    JFrame f = new JFrame("HitTestSample");
    HitTestSample demo = new HitTestSample();
    f.getContentPane().add(demo, "Center");
    f.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    f.setSize(new Dimension(400, 250));
    f.setVisible(true);
  }
}



Paragraph Layout

     
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ParagraphLayout extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    String s = "Java components and products Directory for Java components " +
        "and applications.Hundreds of Java components and applications " +
        "are organized by topic. You can find what you need easily. " +
        "You may also compare your product with others. If your component " +
        "is not listed, just send your url to jexp@jexp.ru. " +
        "http://www.jexp.ru";
    Font font = new Font("Serif", Font.PLAIN, 24);
    AttributedString as = new AttributedString(s);
    as.addAttribute(TextAttribute.FONT, font);
    AttributedCharacterIterator aci = as.getIterator();
    FontRenderContext frc = g2.getFontRenderContext();
    LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc);
    Insets insets = getInsets();
    float wrappingWidth = getSize().width - insets.left - insets.right;
    float x = insets.left;
    float y = insets.top;
    while (lbm.getPosition() < aci.getEndIndex()) {
      TextLayout textLayout = lbm.nextLayout(wrappingWidth);
      y += textLayout.getAscent();
      textLayout.draw(g2, x, y);
      y += textLayout.getDescent() + textLayout.getLeading();
      x = insets.left;
    }
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new ParagraphLayout());
    f.setSize(350, 250);
    f.show();
  }
}



Renders a paragraph of text (line breaks ignored) to an image (created and returned).

  
/*
 *
 * Created on March 16, 2007, 4:34 PM
 *
 * Copyright 2006-2007 Nigel Hughes
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at http://www.apache.org/
 * licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.image.BufferedImage;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.Hashtable;
/**
 * @author nigel
 */
public class Utils {
  /** 
   * Renders a paragraph of text (line breaks ignored) to an image (created and returned). 
   *
   * @param font The font to use
   * @param textColor The color of the text
   * @param text The message
   * @param width The width the text should be limited to
   * @return An image with the text rendered into it
   */
  public static BufferedImage renderTextToImage(Font font, Color textColor, String text, int width){
      Hashtable   map = new Hashtable();
      map.put(TextAttribute.FONT, font);
      AttributedString attributedString = new AttributedString(text,map);
      AttributedCharacterIterator paragraph = attributedString.getIterator();
      
      FontRenderContext frc = new FontRenderContext(null, false, false);
      int paragraphStart = paragraph.getBeginIndex();
      int paragraphEnd = paragraph.getEndIndex();
      LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(paragraph, frc);      
      
      float   drawPosY=0;
      
      //First time around, just determine the height
      while (lineMeasurer.getPosition() < paragraphEnd) {
          TextLayout layout = lineMeasurer.nextLayout(width);
          
          // Move it down
          drawPosY += layout.getAscent() + layout.getDescent() + layout.getLeading();
      }
      
      BufferedImage image = createCompatibleImage(width,(int) drawPosY);
      Graphics2D graphics = (Graphics2D) image.getGraphics();
      graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
      
      drawPosY=0;
      lineMeasurer.setPosition(paragraphStart);
      while (lineMeasurer.getPosition() < paragraphEnd) {
          TextLayout layout = lineMeasurer.nextLayout(width);
          
          // Move y-coordinate by the ascent of the layout.
          drawPosY += layout.getAscent();
          
         /* Compute pen x position.  If the paragraph is
            right-to-left, we want to align the TextLayouts
            to the right edge of the panel.
          */
          float drawPosX;
          if (layout.isLeftToRight()) {
              drawPosX = 0;
          } else {
              drawPosX = width - layout.getAdvance();
          }
          
          // Draw the TextLayout at (drawPosX, drawPosY).
          layout.draw(graphics, drawPosX, drawPosY);
          
          // Move y-coordinate in preparation for next layout.
          drawPosY += layout.getDescent() + layout.getLeading();
      }
      
      graphics.dispose();
      return image;
  }
  /**
   * Creates an image compatible with the current display
   * 
   * @return A BufferedImage with the appropriate color model
   */
  public static BufferedImage createCompatibleImage(int width, int height) {
    GraphicsConfiguration configuration = GraphicsEnvironment.getLocalGraphicsEnvironment()
        .getDefaultScreenDevice().getDefaultConfiguration();
    return configuration.createCompatibleImage(width, height, Transparency.TRANSLUCENT);
  }
}



TextAttribute: color and font

     
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class IteratorTest extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    String s = "Java Source and Support";
    Dimension d = getSize();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    Font serifFont = new Font("Serif", Font.PLAIN, 48);
    Font sansSerifFont = new Font("Monospaced", Font.PLAIN, 48);
    AttributedString as = new AttributedString(s);
    as.addAttribute(TextAttribute.FONT, serifFont);
    as.addAttribute(TextAttribute.FONT, sansSerifFont, 2, 5);
    as.addAttribute(TextAttribute.FOREGROUND, Color.red, 5, 6);
    as.addAttribute(TextAttribute.FOREGROUND, Color.red, 16, 17);
    g2.drawString(as.getIterator(), 40, 80);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new IteratorTest());
    f.setSize(850, 250);
    f.show();
  }
}



TextAttribute: Underline and strike through

     
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class IteratorUnderStrike extends JPanel{
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    String s = "\"www.jexp.ru\" is great.";
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    Font plainFont = new Font("Times New Roman", Font.PLAIN, 24);
    AttributedString as = new AttributedString(s);
    as.addAttribute(TextAttribute.FONT, plainFont);
    as.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 1,
        15);
    as.addAttribute(TextAttribute.STRIKETHROUGH,
        TextAttribute.STRIKETHROUGH_ON, 18, 25);
    g2.drawString(as.getIterator(), 24, 70);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new IteratorUnderStrike());
    f.setSize(850, 250);
    f.show();
  }
}



TextHitInfo Demo: tell you which is the letter you are clicking

     
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.TextHitInfo;
import java.awt.font.TextLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TextHitInfoDemo extends JPanel {
  private TextLayout mTextLayout;
  private int mX = 40, mY = 80;
  public TextHitInfoDemo() {
    addMouseListener(new MouseAdapter() {
      public void mouseClicked(MouseEvent me) {
        TextHitInfo hit = mTextLayout.hitTestChar(me.getX() - mX, me
            .getY()
            - mY);
        System.out.println(hit);
      }
    });
  }
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    String s = "Java Source and Support";
    Font font = new Font("Serif", Font.PLAIN, 32);
    if (mTextLayout == null) {
      FontRenderContext frc = g2.getFontRenderContext();
      mTextLayout = new TextLayout(s, font, frc);
    }
    mTextLayout.draw(g2, mX, mY);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new TextHitInfoDemo());
    f.setSize(200, 200);
    f.show();
  }
  
}



TextLayout demo

     
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TextLayoutOne extends JPanel {
  public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
        RenderingHints.VALUE_ANTIALIAS_ON);
    String s = "Java Source and Support.";
    Font font = new Font("Serif", Font.PLAIN, 32);
    TextLayout textLayout = new TextLayout(s, font, g2
        .getFontRenderContext());
    textLayout.draw(g2, 40, 80);
  }
  public static void main(String[] args) {
    JFrame f = new JFrame();
    f.getContentPane().add(new TextLayoutOne());
    f.setSize(300, 200);
    f.setVisible(true);
  }
}



Unicode display

     
import java.awt.Font;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class HelloInJapanese extends JPanel {
  static JFrame frame;
  static String helloInJapanese = "Hello in Japanese is konnichi wa, \u4eca\u65e5\u306f.";
  public HelloInJapanese(String characters) {
    Font theFont = new Font("Bitstream Cyberbit", Font.PLAIN, 20);
    JTextArea area = new JTextArea(characters, 2, 30);
    area.setFont(theFont);
    area.setLineWrap(true);
    JScrollPane scrollpane = new JScrollPane(area);
    add(scrollpane);
  }
  public static void main(String argv[]) {
    HelloInJapanese japanesePanel = new HelloInJapanese(helloInJapanese);
    frame = new JFrame("Hello in Japanese");
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.getContentPane().add("Center", japanesePanel);
    frame.pack();
    frame.setVisible(true);
  }
}



Unicode: test layout

     
/*
Java Internationalization
By Andy Deitsch, David Czarnecki
ISBN: 0-596-00019-7
O"Reilly
*/
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.text.*;
import javax.swing.*;
public class TextLayoutDemo extends JFrame {
  String davidMessage = "David says, \"\u05E9\u05DC\u05D5\u05DD \u05E2\u05D5\u05DC\u05DD\" ";
  String andyMessage = "Andy also says, \"\u05E9\u05DC\u05D5\u05DD \u05E2\u05D5\u05DC\u05DD\" ";
  String textMessage = davidMessage + andyMessage + davidMessage + andyMessage +
                       davidMessage + andyMessage + davidMessage + andyMessage +
                       davidMessage + andyMessage + davidMessage + andyMessage +
                       davidMessage + andyMessage + davidMessage + andyMessage;
  public TextLayoutDemo() {
    super("TextLayoutDemo");
  }
  public void paint(Graphics g) {
    Graphics2D graphics2D = (Graphics2D)g;
    GraphicsEnvironment.getLocalGraphicsEnvironment();
    Font font = new Font("LucidaSans", Font.PLAIN, 14);
    AttributedString messageAS = new AttributedString(textMessage);
    messageAS.addAttribute(TextAttribute.FONT, font);
    AttributedCharacterIterator messageIterator = messageAS.getIterator();
    FontRenderContext messageFRC = graphics2D.getFontRenderContext();
    LineBreakMeasurer messageLBM = new LineBreakMeasurer(messageIterator, messageFRC);
    Insets insets = getInsets();
    float wrappingWidth = getSize().width - insets.left - insets.right;
    float x = insets.left;
    float y = insets.top;
    while (messageLBM.getPosition() < messageIterator.getEndIndex()) {
      TextLayout textLayout = messageLBM.nextLayout(wrappingWidth);
      y += textLayout.getAscent();
      textLayout.draw(graphics2D, x, y);
      y += textLayout.getDescent() + textLayout.getLeading();
      x = insets.left;
    }
  }
  public static void main(String[] args) {
    JFrame frame = new TextLayoutDemo();
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {System.exit(0);}
    });
    frame.pack();
    frame.setVisible(true);
  }
}