Reputation: 93
I have a funny problem trying to export custom Java JPanels to a PNG file. The export process of the components I've been writing up until now have worked flawlessly.
My JPanels include custom-written JComponents (e.g., override paintComponent(Graphics g) and write what I have to).
The export process looks like the following (of the extended JPanel I have):
public void export(File file, int width, int height)
throws IOException
{
Dimension size = getSize();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = image.createGraphics();
draw (g2, new Rectangle (0, 0, width, height));
try {
ImageIO.write(image, "png", file);
} catch (FileNotFoundException e) {
throw new IOException ("Unable to export chart to ("
+ file.getAbsolutePath() + "): " + e.getLocalizedMessage());
} finally {
g2.dispose();
}
}
The 'draw()' method above causes all of the JPanel's child components to be re-drawn using the new size of the image to be exported. Works very well.
The problem I have today is that I have one custom JPanel that includes some Swing components (a JScrollPane wrapping a JEditorPane). This JPanel includes one of my custom JComponents and then this second JComponent with the JScrollPane on it.
About 75% of the time, this second JComponent with the JScrollPane is not positioned correctly in the exported image when I perform the export. It is positioned at Point (0, 0) and the size is what it looks like on the screen. The 'draw()' method for this JComponent looks like the following:
public void draw(Graphics2D g2, Rectangle componentArea) {
scrollPane.setBounds(componentArea);
textArea.setText(null);
sb.append("<html>");
sb.append("<h1 style=\"text-align:center;\">" + "XXXXXXXXX XXXXXXX" + "</h1>");
textArea.setText(sb.toString());
super.paintComponents(g2);
}
But about 25% of the time this works - this JComponent with the scrollpane is correctly positioned in my exported image. The re-draw the componment works.
It is like there is some double-buffering going on here that I can't figger out....
Ideas?
Upvotes: 0
Views: 510
Reputation: 5667
Layout of Swing components usually occurs lazily rather than immediately so that might be causing your intermittent behaviour. You could try calling scrollPane.doLayout() directly - usually this is a bad idea but it should guarantee that scrollPane is laid out before you paint it.
Also for painting to an off-screen image you should probably call printAll(g) rather than paintComponents(g) as that avoids double buffering issues.
Upvotes: 0
Reputation: 93
Found a solution!! Your comments about 'scheduling painting events' rang around in my head for a bit. I had a problem like this some years ago and forgot about that. Old age will do that....
The solution is to wrap the 'draw()' method in a 'SwingUtilities.invokeAndWait()'. Voila! My 'export()' method now looks like:
public void export(File file, final int width, final int height)
throws IOException
{
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2 = image.createGraphics();
// Must wait for the bloody image to be drawn as Swing 'paint()' methods
// merely schedule painting events. The 'draw()' below may not complete
// the painting process before the 'write()' of the image is performed.
// thus, we wait....
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
draw (g2, new Rectangle (0, 0, width, height));
}
});
ImageIO.write(image, "png", file);
} catch (FileNotFoundException e) {
throw new IOException ("Unable to export chart to ("
+ file.getAbsolutePath() + "): " + e.getLocalizedMessage());
} catch (InterruptedException e) {
e.printStackTrace();
throw new IOException ("Unable to export chart to ("
+ file.getAbsolutePath() + "): " + e.getLocalizedMessage());
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new IOException ("Unable to export chart to ("
+ file.getAbsolutePath() + "): " + e.getLocalizedMessage());
} finally {
g2.dispose();
}
}
Whew!
Upvotes: 0
Reputation: 324197
I would try invoking the paint() method, not paintComponents().
Maybe because you are setting the text of the editor pane the text hasn't been properly parsed and the Document isn't in a final state when you attempt to paint the component. Or maybe because you are dynamically setting the bounds of components you have problems. Try wrapping the super.paint() method in a SwingUtilities.invokeLater().
The ScreenImage class is what I use to create images. But I've always used it to create images of static GUI's. That is I don't alter the bounds of components at the same time a try to create an image.
Upvotes: 0
Reputation: 11592
Do you by any change modify the Transform object of the provided Graphics object in your custom Component? If you do make sure to save it first and then modify a new instance for your purposes and when you are done, set the old Transform back.
Upvotes: 0