MasterBlaster
MasterBlaster

Reputation: 968

CSS Padding not Rendered in JTextPane

I am using a JTextPane to render some HTML. I am using a HTMLEditorKit and a StyleSheet to add rules to the code property so it looks exactly like the code on StackOverflow. The problem is that the JTextPane doesn't render the padding on the code (1 pixel top/bottom, 5 pixels left/right), even though the padding property is listed as one of the properties that is supported by the rendering engine in the documentation.

Here is what I expect to see (As shown by this CSSDeck Lab): Expected Output
                                ↑-↑                    ↑-↑
EDIT:
I have switched from using the <code> tags in my HTML and have switched to <span> by the suggestion of @Sharcoux. This made the font size of the used-to-be-code the same as the text in the <pre> tags, making the red background have the same height as the green border, however, the expected result still isn't produced. Here is the original image if you are interested.

This is what actually appears: Actual Output

I have also tried moving the CSS inline with the text, but to no avail.

MCVE (Also Edited):

import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.StyleSheet;

public class CodePaddingMCVE {

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            JFrame frame = new JFrame("Code Padding Test");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

            JTextPane tp = new JTextPane();
            tp.setContentType("text/html");

            HTMLEditorKit kit = new HTMLEditorKit();
            tp.setEditorKit(kit);
            StyleSheet sheet = kit.getStyleSheet();
            sheet.addRule("span {padding: 1px 5px; background-color: red}"); //Using span here
            sheet.addRule("pre {padding: 0px; background-color: green}");
            tp.setDocument(kit.createDefaultDocument());
            tp.setText("<html><pre>NotCode<span>Code</span>NotCode</pre></html>"); //Using <span> here too

            JScrollPane sp = new JScrollPane(tp) {
                private static final long serialVersionUID = 1L;

                @Override
                public Dimension getPreferredSize() {
                    return new Dimension(250, 50);
                }

            };

            frame.getContentPane().add(sp);
            frame.pack();
            frame.setVisible(true);
        });
    }

}

I am using OS X Yosemite 10.10.15, Eclipse Mars.2 4.5.2, and Java 8 [1.8.0_66].

Upvotes: 1

Views: 637

Answers (1)

Sharcoux
Sharcoux

Reputation: 6085

You're right, the margin and padding seem to not be applied to inline elements. You have 3 options.

1) give up JTextPane and use JavaFX WebView instead.

2) give up HTMLDocument and use another implementation.

3) try to extend HTMLFactory in HTMLEditorKit, and extend InlineView. It would give you something of that sort:

import java.awt.Dimension;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
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.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.InlineView;
import javax.swing.text.html.StyleSheet;

public class CodePaddingMCVE {

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                JFrame frame = new JFrame("Code Padding Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

                JTextPane tp = new JTextPane();
                tp.setContentType("text/html");

                HTMLEditorKit kit = new HTMLEditorKit() {
                    public ViewFactory getViewFactory() {
                        return new HTMLFactory() {
                            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 instanceof HTML.Tag) {
                                    HTML.Tag kind = (HTML.Tag) o;
                                    if (kind == HTML.Tag.CONTENT) {
                                        return new InlineView(elem) {
                                            private short left;
                                            private short right;
                                            private short top;
                                            private short bottom;
                                            protected void setPropertiesFromAttributes() {
                                                AttributeSet attr = getAttributes();
                                                if (attr != null) {
                                                    top = (short) StyleConstants.getSpaceAbove(attr);
                                                    left = (short) StyleConstants.getLeftIndent(attr);
                                                    bottom = (short) StyleConstants.getSpaceBelow(attr);
                                                    right = (short) StyleConstants.getRightIndent(attr);
                                                }
                                                super.setPropertiesFromAttributes();
                                            }
                                            //TODO : use the top, left, bottom and right properties to draw the margin/padding
                                        };
                                    }
                                }
                                return super.create(elem);
                            }
                        };
                    }
                };
                tp.setEditorKit(kit);
                StyleSheet sheet = kit.getStyleSheet();
                sheet.addRule("span {padding: 5px 5px 5px 5px; background-color: red}"); //Using span here
                sheet.addRule("pre {padding: 0px; background-color: green}");
                tp.setDocument(kit.createDefaultDocument());
                tp.setText("<html><pre>NotCode<span>Code</span>NotCode</pre></html>"); //Using <span> here too

                JScrollPane sp = new JScrollPane(tp) {
                    private static final long serialVersionUID = 1L;

                    @Override
                    public Dimension getPreferredSize() {
                        return new Dimension(250, 50);
                    }

                };

                frame.getContentPane().add(sp);
                frame.pack();
                frame.setVisible(true);
            }
        });
    }
}

If you wish to pursue this path, you will need to dig inside the LabelView and GlyphView to see how you can implement a padding or a margin. Maybe others could help finish this.

Good luck.

Upvotes: 1

Related Questions