Search code examples
javaswingfontsjtextareacss

Change line spacing in JTextArea (not JTextPane)


I'm trying to find a way to change the line spacing in a JTextArea component.

A little bit of searching always seems to reveal the same answer: "Use a JTextPane instead and then call setParagraphAttributes".

But I'm wondering whether it's possible to achieve this with just a JTextArea by, for example, messing with the font.

With the deriveFont(...) method, it's possible to change the tracking and the kerning of the font, i.e. the horizontal spacing between characters, but I haven't been able to find a way to change the vertical spacing (maybe ascent, descent, leading). Am I missing something there?


Solution

  • As camickr pointed out, JTextArea does not provide a way to change the line height directly. It simply uses the font height provided by the corresponding FontMetrics.

    But, this leaves a point of attack open using the following helper-class:

    public class FontMetricsWrapper extends FontMetrics {
    
        private final FontMetrics target;
    
        public FontMetricsWrapper(FontMetrics target) {
            super(target.getFont());
            this.target = target;
        }
    
        @Override
        public int bytesWidth(byte[] data, int off, int len) {
            return target.bytesWidth(data, off, len);
        }
    
        @Override
        public int charWidth(char ch) {
            return target.charWidth(ch);
        }
    
        @Override
        public int charWidth(int codePoint) {
            return target.charWidth(codePoint);
        }
    
        // ... tons more @Override's, all of the same form:
        //
        // @Override
        // public baz foo(bar, ...) {
        //     return target.foo(bar, ...);
        // }
    
    }
    

    Then, it becomes possible to create the JTextArea like this:

    JTextArea myTextArea = new JTextArea("Some text") {
        @Override
        public FontMetrics getFontMetrics(Font font) {
            return new FontMetricsWrapper(super.getFontMetrics(font)) {
                @Override
                public int getHeight() {
                    return 10;  // Gives line height in pixels
                }
            };
        }
    };
    

    This is definitely not the cleanest solution and is merely meant as proof of concept. For example, one issue is that getFontMetrics(...) is called quite often, and, in the given example, creates a new instance of the wrapper class each time. So, at the very least, a HashMap that caches the created FontMetricsWrapper for each given font would be in order...

    But, what I was really hoping for was a way to play with the Font or maybe the associated FontRenderContext passed into the JTextArea to modify the line height. For example, is there some way to influence font's reported ascent, descent, and leading values? Seems strange that you can change pretty much any other aspect of the font's appearance, except this one...