Setting JTextPane Font and Color




Swing’s javax.swing.JTextPane class represents a significant improvement over AWT’s TextArea. It allows complex combinations of character and paragraph attributes, embedded images and components, and other features that let you build styled text editors. Unfortunately, however, while JTextPane makes complex things possible, it makes some simple things difficult. Two examples are setting the default font and color.

Figure 1 shows an example of creating an AWT TextArea that uses a 20-point blue serif font.



import java.awt.TextArea;
import java.awt.Frame;

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowAdapter;

import java.awt.Color;
import java.awt.Font;
import java.awt.BorderLayout;

public class Example1 extends WindowAdapter {

    public Example1() {
        Frame f = new Frame("TextArea Example");
        f.setLayout(new BorderLayout());

        // Make a text area, set its font and color, then add it to the frame
        TextArea text = new TextArea();
        Font font = new Font("Serif", Font.ITALIC, 20);
        text.setFont(font);
        text.setForeground(Color.blue);
        f.add(text, BorderLayout.CENTER);

        // Listen for the user to click the frame's close box
        f.addWindowListener(this);
        f.setSize(400, 400);
        f.show();
    }

    public void windowClosing(WindowEvent evt) {
        System.exit(0);
    }

    public static void main(String[] args) {
        Example1 instance = new Example1();
    }

}



Figure 1. Setting text font and color on an AWT TextArea

The code in Figure 1 includes a call to TextArea‘s setFont(…) and setForeground(…) methods, which set the font and color of all of the text in the component. Figure 2 shows a (seemingly) equivalent example using Swing’s JFrame and JTextPane classes.



import javax.swing.JTextPane;
import javax.swing.JFrame;

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowAdapter;

import java.awt.Color;
import java.awt.Font;
import java.awt.BorderLayout;

public class Example2 extends WindowAdapter {

    public Example2() {
        JFrame f = new JFrame("First JTextPane Example");
        f.getContentPane().setLayout(new BorderLayout());

        // Make a text pane, attempt to set its font and color, then 
        // add it to the frame
        JTextPane text = new JTextPane();
        Font font = new Font("Serif", Font.ITALIC, 20);
        text.setFont(font);
        text.setForeground(Color.blue);
        f.getContentPane().add(text, BorderLayout.CENTER);

        // Listen for the user to click the frame's close box
        f.addWindowListener(this);
        f.setSize(400, 400);
        f.show();
    }

    public void windowClosing(WindowEvent evt) {
        System.exit(0);
    }

    public static void main(String[] args) {
        Example2 instance = new Example2();
    }

}



Figure 2. Seemingly equivalent version of Figure 1 using a Swing JTextPane

The code in Figure 2 does not produce the desired result: The text pane uses the default font and color (defined by the current Swing look-and-feel), rather than the font and color passed to the setFont(…) and setForeground(…) methods. The explanation for this lies in JTextPane‘s flexibility. Since each character can have a different font and color, the notion of setting the font and color for all of the text does not make as much sense as it does in the case of the less-flexible TextArea.

The trick is to set the character attributes for the entire text content. If the text pane is empty, the character attributes will be used for any text entered later. Figure 3 shows an example of this. The static method setJTextPaneFont(…) contains the logic for updating a JTextPane‘s attributes from Font and Color objects.



import javax.swing.JTextPane;
import javax.swing.JFrame;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyleConstants;
import javax.swing.text.MutableAttributeSet;

import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowAdapter;

import java.awt.Color;
import java.awt.Font;
import java.awt.BorderLayout;

public class Example3 extends WindowAdapter {

    public Example3() {
        JFrame f = new JFrame("Second JTextPane Example");
        f.getContentPane().setLayout(new BorderLayout());

        // Make a text pane, set its font and color, then add it to the frame
        JTextPane text = new JTextPane();
        Font font = new Font("Serif", Font.ITALIC, 20);
        setJTextPaneFont(text, font, Color.blue);
        f.getContentPane().add(text, BorderLayout.CENTER);

        // Listen for the user to click the frame's close box
        f.addWindowListener(this);
        f.setSize(400, 400);
        f.show();
    }

    /**
     * Utility method for setting the font and color of a JTextPane. The
     * result is roughly equivalent to calling setFont(...) and 
     * setForeground(...) on an AWT TextArea.
     */
    public static void setJTextPaneFont(JTextPane jtp, Font font, Color c) {
        // Start with the current input attributes for the JTextPane. This
        // should ensure that we do not wipe out any existing attributes
        // (such as alignment or other paragraph attributes) currently
        // set on the text area.
        MutableAttributeSet attrs = jtp.getInputAttributes();

        // Set the font family, size, and style, based on properties of
        // the Font object. Note that JTextPane supports a number of
        // character attributes beyond those supported by the Font class.
        // For example, underline, strike-through, super- and sub-script.
        StyleConstants.setFontFamily(attrs, font.getFamily());
        StyleConstants.setFontSize(attrs, font.getSize());
        StyleConstants.setItalic(attrs, (font.getStyle() & Font.ITALIC) != 0);
        StyleConstants.setBold(attrs, (font.getStyle() & Font.BOLD) != 0);

        // Set the font color
        StyleConstants.setForeground(attrs, c);

        // Retrieve the pane's document object
        StyledDocument doc = jtp.getStyledDocument();

        // Replace the style for the entire document. We exceed the length
        // of the document by 1 so that text entered at the end of the
        // document uses the attributes.
        doc.setCharacterAttributes(0, doc.getLength() + 1, attrs, false);
    }

    public void windowClosing(WindowEvent evt) {
        System.exit(0);
    }

    public static void main(String[] args) {
        Example3 instance = new Example3();
    }

}



Figure 3. Setting font and color on a JTextPane

The example in Figure 3 will produce 20-point blue text within the JTextPane.

Of course, JTextPane is not intended as a direct replacement for the AWT TextArea. Swing includes a JTextArea class that mimics TextArea‘s behavior much more closely, and provides direct source code compatibility. There are, however, reasons to use JTextPane instead of JTextArea even in cases where you do not need to support multiple fonts within the text component. For example, JTextArea does not support embedded images or alternate text alignment.