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 compone开发者_如何转开发nt:
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.
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.
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
精彩评论