简体   繁体   中英

Paint RTF/HTML strings into a custom swing component

In my Swing application, users enter styled text into a JTextPane which uses an RTFEditorKit (HTML is also a possibility).

I then need to render many of these styled notes at specific coordinates in a custom component.

I would think the View.paint method would be helpful here, but I'm not able to create a usable View object.

I have the following method:

public View createView() throws IOException, BadLocationException {
 RTFEditorKit kit = new RTFEditorKit();
 final Document document = kit.createDefaultDocument();
 kit.read(new ByteArrayInputStream(text.getBytes("UTF-8")), document, 0);
 return kit.getViewFactory().create(document.getDefaultRootElement());
}

This returns a javax.swing.text.BoxView with the following attributes:

majorAxis = 1
majorSpan = 0
minorSpan = 0
majorReqValid = false
minorReqValid = false
majorRequest = null
minorRequest = null
majorAllocValid = false
majorOffsets = {int[0]@2321}
majorSpans = {int[0]@2322}
minorAllocValid = false
minorOffsets = {int[0]@2323}
minorSpans = {int[0]@2324}
tempRect = {java.awt.Rectangle@2325}"java.awt.Rectangle[x=0,y=0,width=0,height=0]"
children = {javax.swing.text.View[1]@2326}
nchildren = 0
left = 0
right = 0
top = 0
bottom = 0
childAlloc = {java.awt.Rectangle@2327}"java.awt.Rectangle[x=0,y=0,width=0,height=0]"
parent = null
elem = {javax.swing.text.DefaultStyledDocument$SectionElement@2328}"BranchElement(section) 0,35\n"

Note that parent = null and nchildren = 0. This means there's nothing really useful there. I can hack together something by calling JTextPane.getUI().paint , but the text pane needs to be visible, and this feels like the wrong way to do it.

Is there any way to get a visual representation of the RTF content without rendering the actual JTextPane?

This code sort of works, but seems less than ideal. Is there a better way to do it? Also, what's a good way to render the text somewhere other than 0,0 on the graphics?

private static void testRtfRender() {
    String s = "{\\rtf1\\ansi\n" +
            "{\\fonttbl\\f0\\fnil Monospaced;\\f1\\fnil Lucida Grande;}\n" +
            "\n" +
            "\\f1\\fs26\\i0\\b0\\cf0 this is a \\b test\\b0\\par\n" +
            "}";

    JTextPane pane = new JTextPane();
    pane.setContentType("text/rtf");
    pane.setText(s);

    final Dimension preferredSize = pane.getUI().getPreferredSize(pane);
    int w = preferredSize.width;
    int h = preferredSize.height;

    pane.setSize(w, h);
    pane.addNotify();
    pane.validate();

    // would be nice to use this box view instead of instantiating a UI
    // however, unless you call setParent() on the view it's useless
    // What should the parent of a root element be?
    //BoxView view = (BoxView) pane.getEditorKit().getViewFactory().create(pane.getStyledDocument().getDefaultRootElement());
    //view.paint(d, new Rectangle(w, h));

    BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
    final Graphics2D d = img.createGraphics();
    d.setClip(0, 0, w, h); // throws a NullPointerException if I leave this out
    pane.getUI().paint(d, pane);
    d.dispose();
    JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
}

Check out the ScreenImage class which allows you to create a BufferedImage of any Swing component. It should also work for Swing components that are not visible, but yes you do have to do the rendering first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM