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 to the printer, the scroll pane cuts off, and if I resize the frame to the size of the contents of the scroll pane, some sort of internal barrier stops the JFrame becoming more than about 1100 pixels tall.
Another alternative would be to create the content pane of the dialog without attaching it to the root JFrame, because the JPanel's size is not limited in that case. But to get the components to lay themselves out and resize to their proper sizes, I seem to need to make the panel displayable, which means at the very least adding it to a JFrame and calling JFrame.pack(), but again, the 1100 pixel limit comes back.
Here's my code for printing the component:
public static void print(final Component comp) {
final float SCALE = .5f;
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(new Printable() {
public int print(Graphics g, PageFormat pf, int page)
throws PrinterException
{
if (page * pf.getImageableHeight() >= SCALE * comp.getHeight())
return NO_SUCH_PAGE;
((Graphics2D)g).translate(pf.getImageableX(), pf.getImageableY()
- page * pf.getImageableHeight());
((Graphics2D)g).scale(SCALE, SCALE);
comp.printAll(g);
return PAGE_EXISTS;
}
});
if (job.printDialog())
try { job.print(); }
catch (PrinterException ex) {}
}
If I do this, the component has zero size:
JPanel c = createPanel(); // This JPanel has a JScrollPane in it with its
// preferredSize equal to that of its viewport component
// (which is not what I do to show the dialog normally)
print(c);
If I do this, the component has the right size, but prints as solid gray because the sub-components have not been laid out:
JPanel c = createPanel();
c.setSize(c.getPeferredSize());
print(c);
These don't seem to make a difference:
JPanel c = createPanel();
c.validate();
c.revalidate();
c.repaint();
print(c);
This makes the panel larger, but it stops at about a page and a half large (1100px):
JPanel c = createPanel();
JFrame f = new JFrame();
f.setContentPane(c);
f.pack();
print(c);
I'm running out of permutations here. Does anyone know either (a) how to change the OS maximum frame size, (b) how to layout and paint an off-screen component, or (c) how to print a Swing component directly, without having to paint it (?). Help is appreciated.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With