JTextPane vertically aligning text and <img> images with html

538 views Asked by At

I have mostly tried everything so far, I also read, that the HTML render of Java is really limited, and just can do like HTML 3/2 or something like that. So mostly all modern CSS styles aren't working. Basically I need small icons (10x10px) vertically aligned with text over several rows, and I can't find a working solution. The only workaround I have found is create a table:

<table><tr><td>text</td><td><img></td><td>text</td></tr></table>

This works, but just for single line, if there is a line wrap, it doesn't work obviously anymore.

All other solutions which normally work in a browser don't work in a JTextPane, for example setting style="float: left;" for a table in a div, or just using vertical-align: middle; with the img and div elements.

Is there maybe a "dirty" way to manipulate the getViewFactory of the HTMLEditorKit to return all ImageViews few pixels below where they should be rendered?

1

There are 1 answers

3
Sharcoux On

This is how you can edit the HTMLFactory. Here I change the InlineView to make me able to strike through it diagonally. Use it for your own purpose.

import java.awt.BasicStroke;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.Stroke;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;

/**
 *
 * @author François Billioud
 */
public class HTMLBetterEditorKit extends HTMLEditorKit {

    private final HTMLEditorKit.HTMLFactory factory = new HTMLBetterFactory();
    @Override
    public ViewFactory getViewFactory() {
        return factory;
    }

    public static class HTMLBetterFactory extends HTMLEditorKit.HTMLFactory {
        @Override
        public View create(Element elem) {
            AttributeSet attrs = elem.getAttributes();
            Object elementName = attrs.getAttribute(AbstractDocument.ElementNameAttribute);
            Object o = (elementName != null) ? null : attrs.getAttribute(StyleConstants.NameAttribute);
            if (o == HTML.Tag.CONTENT) {
                return new StrikeView(elem);
            }
            return super.create(elem);
        }

        private class StrikeView extends InlineView {
            public StrikeView(Element elem) { super(elem); }

            public void paint(Graphics g, Shape allocation) {
                setStrikeThrough(false);//We will do that ourselves
                super.paint(g, allocation);
                Object textDecorationValue = getAttributes().getAttribute(CSS.Attribute.TEXT_DECORATION);
                if(textDecorationValue!=null && textDecorationValue.toString().equals("line-through")) {
                    paintStrikeLine(g, allocation);
                }
            }

            //paints the strike line
            public void paintStrikeLine(Graphics g, Shape a) {
                int y = a.getBounds().y+a.getBounds().height/2;
                int x1 = (int) a.getBounds().getX();
                int x2 = (int) (a.getBounds().getX() + a.getBounds().getWidth());

                Stroke oldStroke = ((Graphics2D)g).getStroke();
                ((Graphics2D)g).setStroke(new BasicStroke(2));
                g.drawLine(x1, y, x2, y);
                ((Graphics2D)g).setStroke(oldStroke);
            }
        }
    }
}

You just need to set BetterHTMLEditorKit as the EditorKit for your JTextPane.

About how to edit the way your image is rendered, just create a FloatView for instance. The issue is that you will need to edit the way paragraph and text are rendered around the floating image, and so you need to redefine BlockView and InlineView. So it will not be easy work. But if you succeed, don't hesitate to share!

Check the HTMLEditorKit.create method for a better understanding of what is happening during view generation.

Good luck

EDIT: After reading more carefully your question, I realize that you don't want to render a float attribute, but just vertical alignment... For this, you have setAlignmentY. Set it to 0.5 for middle alignment. For JLabel, you might also need to use setVerticalAlignment and setVerticalTextPosition