Imprimindo um grande componente Swing

Eu tenho um formulário Swing com uma tabela personalizada dentro de um JScrollPane (é apenas um JPanel, não uma subclass JTable), e estou tentando obtê-lo para imprimir. Se eu apenas enviar o quadro inteiro para a impressora, o painel de rolagem será cortado, e se eu resize o quadro para o tamanho do conteúdo do painel de rolagem, algum tipo de barreira interna impedirá que o JFrame fique com mais de 1100 pixels de altura.

Outra alternativa seria criar o painel de conteúdo da checkbox de diálogo sem anexá-lo ao JFrame raiz, porque o tamanho do JPanel não é limitado nesse caso. Mas, para que os componentes se organizem e redimensionem para seus tamanhos apropriados, pareço ter que exibir o painel, o que significa, no mínimo, adicioná-lo a um JFrame e chamar JFrame.pack (), mas, novamente, o 1100 o limite de pixels volta.

Aqui está o meu código para imprimir o componente:

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) {} } 

Se eu fizer isso, o componente tem tamanho zero:

 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); 

Se eu fizer isso, o componente tem o tamanho certo, mas imprime como cinza sólido porque os subcomponentes não foram definidos:

 JPanel c = createPanel(); c.setSize(c.getPeferredSize()); print(c); 

Estes não parecem fazer diferença:

 JPanel c = createPanel(); c.validate(); c.revalidate(); c.repaint(); print(c); 

Isso torna o painel maior, mas ele pára em cerca de uma página e meia grande (1100px):

 JPanel c = createPanel(); JFrame f = new JFrame(); f.setContentPane(c); f.pack(); print(c); 

Estou ficando sem permutas aqui. Alguém sabe quer (a) como alterar o tamanho máximo de quadro OS, (b) como layout e pintar um componente fora da canvas, ou (c) como imprimir um componente Swing diretamente, sem ter que pintá-lo (?) . Ajuda é apreciada.

Usando o método paint() do seu painel personalizado, renderize o conteúdo em um BufferedImage .

Adendo: Aqui está um exemplo mais completo da abordagem, que simplesmente escala o componente pela metade. Você desejará preservar a proporção em seu aplicativo real.

insira a descrição da imagem aqui

 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(); } }); } } 

Como mostrado aqui , você pode dimensionar a renderização para ajustar-se ao MediaPrintableArea do destino ou usar getSubimage() para dividir o conteúdo em páginas, conforme desejado.

você tem que imprimir o componente sem o scrollpane. o scrollpane sempre imprime apenas o conteúdo visível.

se você não tiver access ao método createPanel (), e o painel desse método contiver apenas o scrollpane, você poderá obter o componente, que o scrollpane contém, da seguinte maneira:

 JPanel panel = createPanel(); print(((JScrollPane)panel.getComponents()[0]).getViewport()); 

mas se os seus dados são formatados como uma tabela, você também pode usar o JTable. o JTable possui um poderoso método print (). mais informações em http://download.oracle.com/javase/tutorial/uiswing/misc/printtable.html

Como um componente em um JScrollPane pode ter tamanho arbitrário, mesmo depois de se tornar exibível, Minha solução é tentar isto:

 JPanel c = createPanel(); JFrame f = new JFrame(); f.getContentPane().add(new JScrollPane(c)); f.pack(); print(c); 

para que eu possa validar o JPanel sem que ele seja limitado ao tamanho máximo de um JFrame. Ele também tem a aparência de “resolução ilimitada” nas fonts e as coisas que você obtém ao imprimir os componentes diretamente, sem o uso de buffer duplo como o trashgod sugerido.

Você mencionou na sua pergunta, que talvez você precisaria imprimir um componente invisível. Como isso funciona é mostrado aqui por Java42 , eu apenas rearranjei um pouco:

 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(); } } 

Se você ainda precisar imprimir várias páginas, poderá combinar essa solução com minha resposta sobre como imprimir um Component em várias páginas.