Swing HTML rendering shows very large bullet point
Asked Answered
T

2

5

I use a JEditorPane to render some HTML in a swing application. I use a bullet point list <ul><li>...</li></ul> and I obtain overly large bullet points in the output. The same HTML chunk will show normal sized bullet points in a real browser.

I observed this on Windows 7 / JDK 7 and iirc also on Ubuntu and OpenJDK 1.7.0_09.

Is this known? Is there a way around it?

Working example:

import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.WindowConstants;

/**
 *
 */
public class HTMLTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // create new frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
        frame.setSize(500, 500);

        // create editor pane and fill with some html
        JEditorPane pane = new JEditorPane();
        pane.setContentType("text/html");
        pane.setEditable(false);
        pane.setText("<html><h1>Heading</h1>Text<ul><li>Bullet point</li></ul></html>");

        // add editor pane to frame and set frame visible
        frame.add(pane);
        frame.setVisible(true);
    }
}

P.S.: I am now using

ul {
    list-style-type: none;
    margin-left: 10px
}

in a css file and

<li>&bull; Item 1</li>

in the html to have a decent bullet point. Solution inspired by aterai.

Tva answered 29/11, 2012 at 13:7 Comment(1)
This combines favorably with something like text-indent: -20px; padding-left: 20px to indent the text to theright of the bullet symbol. I haven't found out how to automatically align the indent with the bullet size though.Knawel
M
3

+1 @StanislavL and here is another example(use css list-style-image property):

screenshot

bullet.png: bullet.png

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

public class HTMLTest2 {
  public JComponent makeEditorPane(String bullet) {
    // create editor pane and fill with some html
    JEditorPane pane = new JEditorPane();
    pane.setContentType("text/html");
    pane.setEditable(false);
    if(bullet!=null) {
      HTMLEditorKit htmlEditorKit = (HTMLEditorKit)pane.getEditorKit();
      StyleSheet styleSheet = htmlEditorKit.getStyleSheet();
      //String u = getClass().getResource(bullet).toString();
      String u = "https://i.sstatic.net/jV29K.png";
      styleSheet.addRule(String.format("ul{list-style-image:url(%s);margin:0px 20px;", u));
      //styleSheet.addRule("ul{list-style-type:circle;margin:0px 20px;}");
      //styleSheet.addRule("ul{list-style-type:disc;margin:0px 20px;}");
      //styleSheet.addRule("ul{list-style-type:decimal;margin:0px 20px;}");
    }
    pane.setText("<html><h1>Heading</h1>Text<ul><li>Bullet point</li></ul></html>");
    return pane;
  }
  public JComponent makeUI() {
    JPanel p = new JPanel(new GridLayout(2,1));
    p.add(new JScrollPane(makeEditorPane(null)));
    p.add(new JScrollPane(makeEditorPane("bullet.png")));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new HTMLTest2().makeUI());
    f.setSize(320, 320);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
Merge answered 29/11, 2012 at 13:38 Comment(3)
I marked this answer as solution because it seem a bit easier to realize than the other from StanislavL, although both are good.Tva
@Merge Is there a simpler solution to this now five years on, have Oracle addressed this is any way.Whinstone
@PaulTaylor CSS support in the Swing component has not changed since 5 years ago.Merge
O
6

See ListView source code

public void paint(Graphics g, Shape allocation) {
    super.paint(g, allocation);
    Rectangle alloc = allocation.getBounds();
    Rectangle clip = g.getClipBounds();
    // Since listPainter paints in the insets we have to check for the
    // case where the child is not painted because the paint region is
    // to the left of the child. This assumes the ListPainter paints in
    // the left margin.
    if ((clip.x + clip.width) < (alloc.x + getLeftInset())) {
        Rectangle childRect = alloc;
        alloc = getInsideAllocation(allocation);
        int n = getViewCount();
        int endY = clip.y + clip.height;
        for (int i = 0; i < n; i++) {
        childRect.setBounds(alloc);
        childAllocation(i, childRect);
        if (childRect.y < endY) {
            if ((childRect.y + childRect.height) >= clip.y) {
            listPainter.paint(g, childRect.x, childRect.y,
                      childRect.width, childRect.height,
                      this, i);
            }
        }
        else {
            break;
        }
        }
    }
}

The listPainter uses the past arguments as

    if (childtype == CSS.Value.SQUARE || childtype == CSS.Value.CIRCLE
        || childtype == CSS.Value.DISC) {
    drawShape(g, childtype, (int) x, (int) y, 
          (int) w, (int) h, align);
    }

And the drawShape is following as you can see the size is hardcoded there to be 8

void drawShape(Graphics g, CSS.Value type, int ax, int ay, int aw, 
               int ah, float align) {
            // Align to bottom of shape.
            int gap = isLeftToRight ? - (bulletgap + 8) : (aw + bulletgap);
            int x = ax + gap;
            int y = Math.max(ay, ay + (int)(align * ah) - 8);

        if (type == CSS.Value.SQUARE) {
        g.drawRect(x, y, 8, 8);
        } else if (type == CSS.Value.CIRCLE) {
        g.drawOval(x, y, 8, 8);
        } else {
        g.fillOval(x, y, 8, 8);
        }
    }

You can try to replace the StyleSheet's ListPainter in the method provided your own painter

public ListPainter getListPainter(AttributeSet a) {
    return new ListPainter(a, this);
}

where you can change the rendering of bullets

Osswald answered 29/11, 2012 at 13:24 Comment(3)
Thank you for that very nice search. That effectively makes all list shapes ugly in html rendering in swing. I think in browsers the size of the bullet migt depend on the actual font size.Tva
You can override ListView's 'public void paint(Graphics g, Shape allocation)' method and provide own painting there instead of replacing 'ListPainter'Osswald
But why is the default so ugly ?Whinstone
M
3

+1 @StanislavL and here is another example(use css list-style-image property):

screenshot

bullet.png: bullet.png

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.text.html.*;

public class HTMLTest2 {
  public JComponent makeEditorPane(String bullet) {
    // create editor pane and fill with some html
    JEditorPane pane = new JEditorPane();
    pane.setContentType("text/html");
    pane.setEditable(false);
    if(bullet!=null) {
      HTMLEditorKit htmlEditorKit = (HTMLEditorKit)pane.getEditorKit();
      StyleSheet styleSheet = htmlEditorKit.getStyleSheet();
      //String u = getClass().getResource(bullet).toString();
      String u = "https://i.sstatic.net/jV29K.png";
      styleSheet.addRule(String.format("ul{list-style-image:url(%s);margin:0px 20px;", u));
      //styleSheet.addRule("ul{list-style-type:circle;margin:0px 20px;}");
      //styleSheet.addRule("ul{list-style-type:disc;margin:0px 20px;}");
      //styleSheet.addRule("ul{list-style-type:decimal;margin:0px 20px;}");
    }
    pane.setText("<html><h1>Heading</h1>Text<ul><li>Bullet point</li></ul></html>");
    return pane;
  }
  public JComponent makeUI() {
    JPanel p = new JPanel(new GridLayout(2,1));
    p.add(new JScrollPane(makeEditorPane(null)));
    p.add(new JScrollPane(makeEditorPane("bullet.png")));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() {
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new HTMLTest2().makeUI());
    f.setSize(320, 320);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}
Merge answered 29/11, 2012 at 13:38 Comment(3)
I marked this answer as solution because it seem a bit easier to realize than the other from StanislavL, although both are good.Tva
@Merge Is there a simpler solution to this now five years on, have Oracle addressed this is any way.Whinstone
@PaulTaylor CSS support in the Swing component has not changed since 5 years ago.Merge

© 2022 - 2024 — McMap. All rights reserved.