tags:

views:

230

answers:

2

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?

A: 

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)));
}
Sam Barnum
A: 

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.

camickr
Thanks for the link, this looks more geared towards taking a snapshot of a component which is already visible on-screen. I'd like to use a "stamping" approach to render blocks of styled text in various places on my custom component.
Sam Barnum
I don't know what a stamping apprach is. Maybe I don't understand the question, but idea is you can create an image from a non visible component. You can then make sub images to render on your custom component. The key is you don't need a visible component to create the image.
camickr