1

I have to convert styled text to wrapped simple text (for SVG word wrapping). I cannot beleive that the word wrapping information (how many lines are there, where are the line breaks) cannot be extracted from the JTextArea. So I created a small frame program:

package bla;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;

public class Example1 extends WindowAdapter {
    private static String content = "01234567890123456789\n" + "0123456 0123456 01234567 01234567";
    JTextArea text;

    public Example1() {
        Frame f = new Frame("TextArea Example");
        f.setLayout(new BorderLayout());
        Font font = new Font("Serif", Font.ITALIC, 20);
        text = new JTextArea();
        text.setFont(font);
        text.setForeground(Color.blue);
        text.setLineWrap(true);
        text.setWrapStyleWord(true);
        f.add(text, BorderLayout.CENTER);
        text.setText(content);
        // Listen for the user to click the frame's close box
        f.addWindowListener(this);
        f.setSize(100, 511);
        f.show();
    }

    public static List<String> getLines( JTextArea text ) {
        //WHAT SHOULD I WRITE HERE
        return new ArrayList<String>();
    }

    public void windowClosing(WindowEvent evt) {
        List<String> lines = getLines(text);
        System.out.println( "Number of lines:" + lines.size());
        for (String line : lines) {
            System.out.println( line );
        }
        System.exit(0);
    }

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

}

If you run it you will see this:

The displayed picture is this

And what I expect as output:

Number of lines:6
0123456789
0123456789
0123456
0123456
01234567
01234567

What should I write in place of the comment?

Complete answer:

So, then the complete solution based on the accepted answer without displaying the frame actually (note, that you should remove the actual newline characters from the result):

package bla;

import java.awt.Color;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;
import javax.swing.text.Utilities;

public class Example1 {
    private static String content = "01234567890123456789\n" + "0123456 0123456 01234567 01234567";

    public static List<String> getLines( String textContent, int width, Font font ) throws BadLocationException {
        JTextArea text;
        text = new JTextArea();
        text.setFont(font);
        text.setForeground(Color.blue);
        text.setLineWrap(true);
        text.setWrapStyleWord(true);
        text.setText(content);
        text.setSize(width, 1);
        int lastIndex = 0;
        int index = Utilities.getRowEnd(text, 0);
        List<String> result = new ArrayList<String>();
        do {
            result.add( textContent.substring( lastIndex, Math.min( index+1, textContent.length() ) ) );
            lastIndex = index + 1;
        }
        while ( lastIndex < textContent.length() && ( index = Utilities.getRowEnd(text, lastIndex) ) > 0 );
        return result;
    }

    public static void main(String[] args) throws BadLocationException {
        Font font = new Font("Serif", Font.ITALIC, 20);
        Example1 instance = new Example1();
        for (String line : getLines(content,110,font)) {
            System.out.println( line.replaceAll( "\n", "") );
        }
    }

} 

Open question in me (not that important), why is that a frame with 100px width containing a jtextarea wraps the text later than a jtextarea without frame with the same width. Ideas for this?

Gábor Lipták
  • 9,646
  • 2
  • 59
  • 113
  • 2
    When words wrap might come down to the font face or styling. It is not an inherent property of raw text, and only becomes relevant when you've nailed all the variables to the point it might be a PDF. – Andrew Thompson Jun 02 '12 at 07:00
  • Thats what I want. I want to know, that with a given width, with a given font, where would be my text wrapped. Since there is no word wrapping in SVG, I have to simulate it, but I want to let Swing to calculate it for me. – Gábor Lipták Jun 02 '12 at 10:39
  • You can use `TextLayout`, as suggested [below](http://stackoverflow.com/a/10858941/230513). – trashgod Jun 02 '12 at 10:43

3 Answers3

3

JTextArea does not support styled text, but it does support line-oriented access to its model, PlainDocument, as shown below. For reference,

  • Don't mix AWT (Frame) and Swing (JTextArea) components unnecessarily.
  • Swing GUI objects should be constructed and manipulated only on the event dispatch thread.
  • As noted here, the getLineCount() method counts line.separator delimited lines, not wrapped lines.
  • BasicTextUI handles Look & Feel dependent rendering; the methods viewToModel() and viewToModel() translate between the two coordinate systems.

Console:

01234567890123456789
0123456 0123456
01234567 01234567

Code:

import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.text.BadLocationException;
import javax.swing.text.PlainDocument;

public class Example2 {

    private static final Character SEP = Character.LINE_SEPARATOR;
    private static String content = ""
        + "01234567890123456789" + SEP
        + "0123456 0123456" + SEP
        + "01234567 01234567";
    private JTextArea text;

    public Example2() {
        JFrame f = new JFrame("TextArea Example");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Font font = new Font("Serif", Font.ITALIC, 20);
        text = new JTextArea(4, 8);
        text.setFont(font);
        text.setForeground(Color.blue);
        text.setLineWrap(true);
        text.setWrapStyleWord(true);
        text.setText(content);
        f.add(new JScrollPane(text));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
        for (String s : getLines(text)) {
            System.out.println(s);
        }
    }

    private List<String> getLines(JTextArea text) {
        PlainDocument doc = (PlainDocument) text.getDocument();
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < text.getLineCount(); i++) {
            try {
                int start = text.getLineStartOffset(i);
                int length = text.getLineEndOffset(i) - start;
                list.add(doc.getText(start, length));
            } catch (BadLocationException ex) {
                ex.printStackTrace(System.err);
            }
        }
        return list;
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                Example2 instance = new Example2();
            }
        });
    }
}
Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • The solution in your answer do not return the lines I expect, but returns only two lines according to my newline character. – Gábor Lipták Jun 02 '12 at 08:22
  • 1
    Yes, as noted in comments by @Andrew, myself and in the related [answer](http://stackoverflow.com/a/5998117/230513) cited; more above. – trashgod Jun 02 '12 at 10:41
2

JTextArea does have a : getLines() method, is that not working for you?

Jim Barrows
  • 3,634
  • 1
  • 25
  • 36
2

Use Utilities.getRowEnd() Pass 0 as start offset and then previousline's end offset +1

StanislavL
  • 56,971
  • 9
  • 68
  • 98