Hello everyone,

In my application I use a JTextPane embedded in a JScrollPane to display text which is formatted using HTML tags. At first I used JTextArea but then switched to JTextPane to be able to use HTML and colors. Then I needed the JTextPane to support letter based wrapping instead of just the default wrapping by white space. In other words if the content is too long it should be wrapped (just like Microsoft Word does) and the horizontal scroll bar of the JScrollPane should not be visible.

Therefore, I found a solution in this answer that works perfectly: Letter wrap in html in JEditorPane. It uses a custom HTMLEditorKit for the JTextPane.

But with this solution the JTextPane ignores any line breaks with the HTML tag <br>. I have to admit I don't really know how the code in the solution works. It would be great if someone could tell me how to get the HTML new line <br> tag working with this solution.

Here is a minimal, complete & verifiable example:


Code:
import javax.swing.*;
import javax.swing.text.Element;
import javax.swing.text.ParagraphView;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;
import java.awt.*;

public class JTextPaneTest extends JFrame {

    public static void main(String[] args) {
        new JTextPaneTest();
    }

    public JTextPaneTest(){
        setLayout(new BorderLayout());

        JTextPane textPane = new JTextPane();
        textPane.setContentType("text/html");
        textPane.setEditorKit(new HTMLEditorKitWrapSupport());  // makes JTextPane ignore <br> tag

        // example 1 (JTextPane ignores <br> tag when using the custom HTMLEditorKit)
        textPane.setText("<html><body style='font-size:22pt'>  <p>Line 1 <br> Line 2</p>  </body></html>");

        // example 2 (the text should be wrapped and the JScrollPane's horizontal bar should not be visible)
        //textPane.setText("<html><body style='font-size:25pt'>  <p>LONGWORDWITHOUTSPACES_LONGWORDWITHOUTSPACES_LONGWORDWITHOUTSPACES_LONGWORDWITHOUTSPACES_LONGWORDWITHOUTSPACES_</p>  </body></html>");

        JScrollPane scrollPane = new JScrollPane(textPane);
        add(scrollPane, BorderLayout.CENTER);

        setSize(new Dimension(500, 500));
        setVisible(true);
    }

    class HTMLEditorKitWrapSupport extends HTMLEditorKit {

        @Override
        public ViewFactory getViewFactory() {

            return new HTMLEditorKit.HTMLFactory() {

                public View create(Element element) {

                    View view = super.create(element);

                    if (view instanceof InlineView) {

                        return new InlineView(element) {

                            @Override
                            public int getBreakWeight(int axis, float pos, float len) {
                                return GoodBreakWeight;
                            }

                            @Override
                            public View breakView(int axis, int p0, float pos, float len) {
                                if (axis == View.X_AXIS) {
                                    checkPainter();
                                    int p1 = getGlyphPainter().getBoundedPosition(this, p0, pos, len);
                                    if (p0 == getStartOffset() && p1 == getEndOffset()) {
                                        return this;
                                    }
                                    return createFragment(p0, p1);
                                }
                                return this;
                            }
                        };

                    } else if (view instanceof ParagraphView) {
                        return new ParagraphView(element) {
                            protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements sizeRequirements) {
                                if (sizeRequirements == null) {
                                    sizeRequirements = new SizeRequirements();
                                }

                                float pref = layoutPool.getPreferredSpan(axis);
                                float min = layoutPool.getMinimumSpan(axis);

                                // Don't include insets, Box.getXXXSpan will include them.
                                sizeRequirements.minimum = (int) min;
                                sizeRequirements.preferred = Math.max(sizeRequirements.minimum, (int) pref);
                                sizeRequirements.maximum = Integer.MAX_VALUE;
                                sizeRequirements.alignment = 0.5f;
                                return sizeRequirements;
                            }

                        };
                    }
                    return view;
                }

            };
        }
    }    
}