I have a Swing form with a custom table inside a JScrollPane (it\'s just a JPanel, not a JTable subclass), and I am trying to get it to print. If I just send the whole frame
You mentioned in your question, that maybe you would need to print an invisible component. How this works is shown here by Java42, I just rearranged it a bit:
public class PrintInvisible {
static class JPanelPrintable extends JPanel implements Printable
{
public int print(Graphics g, PageFormat pf, int page) throws PrinterException
{
if (page > 0) return Printable.NO_SUCH_PAGE;
printAll(g);
return Printable.PAGE_EXISTS;
}
public void print() throws PrinterException
{
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(this);
if (job.printDialog()) job.print();
}
};
public static void main(String args[]) throws PrinterException {
final JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setSize(400,400);
final JPanelPrintable j = new JPanelPrintable();
j.setLayout(new BorderLayout());
j.add(new JButton("1111"),BorderLayout.NORTH);
j.add(new JButton("2222"),BorderLayout.SOUTH);
f.add(j);f.repaint();f.pack();
//f.setVisible(true);
j.print();
}
}
If you yet need to print multiple pages, you can combine this solution with my answer on how to print a Component to multiple pages
Using your custom panel's paint()
method, render the content into a BufferedImage.
Addendum: Here's a more complete example of the approach, which simply scales the component by half. You'll want to preserve the aspect ratio in your actual application.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
/** @see https://stackoverflow.com/questions/7026822 */
public class PanelPaint extends JPanel {
private static final double SCALE = 0.5;
public PanelPaint() {
super(new GridLayout(0, 1));
final MyPanel panel = new MyPanel();
JScrollPane scroll = new JScrollPane(panel);
scroll.getViewport().setPreferredSize(new Dimension(320, 240));
this.add(scroll);
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
add(new JLabel(new ImageIcon(createImage(panel))));
}
});
}
private BufferedImage createImage(MyPanel panel) {
Dimension size = panel.getPreferredSize();
BufferedImage image = new BufferedImage(
size.width, size.height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
panel.paint(g2d);
g2d.dispose();
AffineTransform at = new AffineTransform();
at.scale(SCALE, SCALE);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
return scaleOp.filter(image, null);
}
private static class MyPanel extends JPanel {
private static final int N = 16;
public MyPanel() {
super(true);
this.setLayout(new GridLayout(N, N));
for (int i = 0; i < N * N; i++) {
this.add(new JLabel(String.valueOf(i) + " "));
}
}
}
private void display() {
JFrame f = new JFrame("PanelPaint");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new PanelPaint().display();
}
});
}
}
As shown here, you can scale the rendering to fit the destination's MediaPrintableArea, or use getSubimage() to divide the content into pages as desired.
you have to print the component without scrollpane. the scrollpane will always only prints the visible content.
if you havent access to the createPanel() Method, and the panel from this method only contains the scrollpane, you can get the component, which the scrollpane contains, in the following way:
JPanel panel = createPanel();
print(((JScrollPane)panel.getComponents()[0]).getViewport());
but if your data is format as a table, you can also about using the JTable. the JTable has a own powerful print() Method. more infos at http://download.oracle.com/javase/tutorial/uiswing/misc/printtable.html
Since a component in a JScrollPane can have arbitrary size, even after it is made displayable, My solution is to try this:
JPanel c = createPanel();
JFrame f = new JFrame();
f.getContentPane().add(new JScrollPane(c));
f.pack();
print(c);
so that I can validate the JPanel without it being size-limited to the maximum size of a JFrame. It also has the "unlimited resolution" look on the fonts and things that you get from printing the components directly, without double-buffering like trashgod suggested.