Printing a Large Swing Component

Printing a large Swing component

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.

How can I pack() a printable Java Swing component?

You could use invalidate(), which will cause validate() to invoke validateTree(); alternatively, use validateTree() directly.

@TacB0sS seems to make a good point: simply don't invoke setVisible(); this related previous question is cited for reference.

printing a 2 pages of jframe in java

So, the first thing we need to is make sure that the component been printed is at it's preferred size...

component.setSize(component.getPreferredSize());

This is important, but also remember, that this will affect the component that is on the screen...

Next, we need to work out if we are printing a new page or re-printing the current page. This occurs because print might be called multiple times for a given page...

if (lastPage != pageIndex) {
lastPage = pageIndex;
//...
}

We then need to calculate the y offset of the component that fits the current page...

yOffset = height * pageIndex;
if (yOffset > component.getHeight()) {
yOffset = -1;
}

If the yOffset is greater then the component height, then we don't want to print any more pages.

Next, we print the page, to do this, we need to translate the Graphics context so that the yOffset becomes the new 0 position...

g2d.translate(0, -yOffset);

Then we print the component...

component.printAll(g2d);

For example...

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.print.attribute.standard.MediaSizeName;
import javax.print.attribute.standard.PrinterResolution;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.Scrollable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PrintMe {

public static void main(String[] args) {
new PrintMe();
}

public PrintMe() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}

TestPane testPane = new TestPane();

JButton btn = new JButton("Print");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));

PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new MultiPagePrintable(testPane));

if (pj.printDialog(aset)) {
try {
pj.print(aset);
testPane.getParent().invalidate();
testPane.getParent().validate();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
});

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(testPane));
frame.add(btn, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel implements Scrollable {

private BufferedImage img;

public TestPane() {
try {
img = ImageIO.read(new File("Get your own image"));
} catch (IOException ex) {
ex.printStackTrace();
}
}

@Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
g2d.dispose();
}
}

@Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(200, 200);
}

@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}

@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 128;
}

@Override
public boolean getScrollableTracksViewportWidth() {
return false;
}

@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}

}

public class MultiPagePrintable implements Printable {

private JComponent component;
private int lastPage = 0;
private double yOffset;

public MultiPagePrintable(JComponent component) {
this.component = component;
}

@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;

double height = pageFormat.getImageableHeight();
component.setSize(component.getPreferredSize());

if (lastPage != pageIndex) {
lastPage = pageIndex;
yOffset = height * pageIndex;
if (yOffset > component.getHeight()) {
yOffset = -1;
}
}

if (yOffset >= 0) {
Graphics2D g2d = (Graphics2D) graphics;

g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());

g2d.translate(0, -yOffset);
component.printAll(g2d);
result = PAGE_EXISTS;
}
return result;
}

}

}

Now, this example only prints in the vertical direction, if you need it to print the horizontal direction as well, it gets a little more complicated, but that the basic concept remains the same

How can I print a single JPanel's contents?

Here is an example to print any Swing component.

public void printComponenet(Component component){
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setJobName(" Print Component ");

pj.setPrintable (new Printable() {
public int print(Graphics pg, PageFormat pf, int pageNum){
if (pageNum > 0){
return Printable.NO_SUCH_PAGE;
}

Graphics2D g2 = (Graphics2D) pg;
g2.translate(pf.getImageableX(), pf.getImageableY());
component.paint(g2);
return Printable.PAGE_EXISTS;
}
});
if (pj.printDialog() == false)
return;

try {
pj.print();
} catch (PrinterException ex) {
// handle exception
}
}

Fit/Scale JComponent to page being printed

The basic concept is to use an AffineTransformation to provide scaling to the resulting output.

In my tests, I was able to take an image of 7680x4800 and get printed on a page of 595x842 (scaled down by something like 93%)

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class PrintTest {

public static void main(String[] args) {
new PrintTest();
}

public PrintTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

final TestPane imagePane = new TestPane();
JButton print = new JButton("Print");
print.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
printComponent(imagePane);
}
});

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(imagePane);
frame.add(print, BorderLayout.SOUTH);
frame.setSize(200, 200);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class TestPane extends JPanel {

private BufferedImage bg;

public TestPane() {
try {
bg = ImageIO.read(new File("path/to/a/image"));
} catch (IOException ex) {
Logger.getLogger(PrintTest.class.getName()).log(Level.SEVERE, null, ex);
}
}

@Override
public Dimension getPreferredSize() {
return bg == null ? new Dimension(200, 200) : new Dimension(bg.getWidth(), bg.getHeight());
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (bg != null) {
int x = (getWidth() - bg.getWidth()) / 2;
int y = (getHeight() - bg.getHeight()) / 2;
g2d.drawImage(bg, x, y, this);
}
g2d.dispose();
}
}

public void printComponent(Component comp) {
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setJobName(" Print Component ");

pj.setPrintable(new ComponentPrintable(comp));

if (!pj.printDialog()) {
return;
}
try {
pj.print();
} catch (PrinterException ex) {
System.out.println(ex);
}
}

public class ComponentPrintable implements Printable {

private Component comp;

private ComponentPrintable(Component comp) {
this.comp = comp;
}

@Override
public int print(Graphics g, PageFormat pf, int pageNumber)
throws PrinterException {
// TODO Auto-generated method stub
if (pageNumber > 0) {
return Printable.NO_SUCH_PAGE;
}

// Get the preferred size ofthe component...
Dimension compSize = comp.getPreferredSize();
// Make sure we size to the preferred size
comp.setSize(compSize);
// Get the the print size
Dimension printSize = new Dimension();
printSize.setSize(pf.getImageableWidth(), pf.getImageableHeight());

// Calculate the scale factor
double scaleFactor = getScaleFactorToFit(compSize, printSize);
// Don't want to scale up, only want to scale down
if (scaleFactor > 1d) {
scaleFactor = 1d;
}

// Calcaulte the scaled size...
double scaleWidth = compSize.width * scaleFactor;
double scaleHeight = compSize.height * scaleFactor;

// Create a clone of the graphics context. This allows us to manipulate
// the graphics context without begin worried about what effects
// it might have once we're finished
Graphics2D g2 = (Graphics2D) g.create();
// Calculate the x/y position of the component, this will center
// the result on the page if it can
double x = ((pf.getImageableWidth() - scaleWidth) / 2d) + pf.getImageableX();
double y = ((pf.getImageableHeight() - scaleHeight) / 2d) + pf.getImageableY();
// Create a new AffineTransformation
AffineTransform at = new AffineTransform();
// Translate the offset to out "center" of page
at.translate(x, y);
// Set the scaling
at.scale(scaleFactor, scaleFactor);
// Apply the transformation
g2.transform(at);
// Print the component
comp.printAll(g2);
// Dispose of the graphics context, freeing up memory and discarding
// our changes
g2.dispose();

comp.revalidate();
return Printable.PAGE_EXISTS;
}
}

public static double getScaleFactorToFit(Dimension original, Dimension toFit) {

double dScale = 1d;

if (original != null && toFit != null) {

double dScaleWidth = getScaleFactor(original.width, toFit.width);
double dScaleHeight = getScaleFactor(original.height, toFit.height);

dScale = Math.min(dScaleHeight, dScaleWidth);

}

return dScale;

}

public static double getScaleFactor(int iMasterSize, int iTargetSize) {

double dScale = 1;
if (iMasterSize > iTargetSize) {

dScale = (double) iTargetSize / (double) iMasterSize;

} else {

dScale = (double) iTargetSize / (double) iMasterSize;

}

return dScale;

}
}

Printing a number of JPanels

Stripped down example of how to print a JPanel while invisible.

public class TestPrinterSmall  {
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;
}
};
private static void printIt(Printable p) throws PrinterException {
PrinterJob job = PrinterJob.getPrinterJob();
job.setPrintable(p);
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);
printIt(j);
}
}

Output:

(nothing)

Printer:

Sample Image

Send a JPanel to the printer

Check out the Java printing API and tutorial along with JComponent.print(Graphics).

Here is a rudimentary class which will print any component which fits on 1 page (I can't take credit for this, I got the code from Marty Hall's tutorial):

import java.awt.*;
import java.awt.print.*;
import javax.swing.*;

/**
* Generic component printer. This object allows any AWT or Swing component (or DCT system)
* to be printed by performing it pre and post print responsibilities.
* <p>
* When printing components, the role of the print method is nothing more than to scale the Graphics, turn off double
* buffering, and call paint. There is no particular reason to put that print method in the component being printed. A
* better approach is to build a generic printComponent method to which you simply pass the component you want printed.
* <p>
* With Swing, almost all components have double buffering turned on by default. In general, this is a great benefit,
* making for convenient and efficient painting. However, in the specific case of printing, it can is a huge problem.
* First, since printing components relies on scaling the coordinate system and then simply calling the component's
* paint method, if double buffering is enabled printing amounts to little more than scaling up the buffer (off-screen
* image) which results in ugly low-resolution printing like you already had available. Secondly, sending these huge
* buffers to the printer results in huge print spooler files which take a very long time to print. Consequently this
* object globally turns off double buffering before printing and turns it back on afterwards.
* <p>
* Threading Design : [x] Single Threaded [ ] Threadsafe [ ] Immutable [ ] Isolated
*/

public class ComponentPrinter
extends Object
implements Printable
{

// *****************************************************************************
// INSTANCE PROPERTIES
// *****************************************************************************

private Component component; // the component to print

// *****************************************************************************
// INSTANCE CREATE/DELETE
// *****************************************************************************

public ComponentPrinter(Component com) {
component=com;
}

// *****************************************************************************
// INSTANCE METHODS
// *****************************************************************************

public void print() throws PrinterException {
PrinterJob printJob=PrinterJob.getPrinterJob();

printJob.setPrintable(this);
if(printJob.printDialog()) {
printJob.print();
}
}

public int print(Graphics gc, PageFormat pageFormat, int pageIndex) {
if(pageIndex>0) {
return NO_SUCH_PAGE;
}

RepaintManager mgr=RepaintManager.currentManager(component);
Graphics2D g2d=(Graphics2D)gc;

g2d.translate(pageFormat.getImageableX(),pageFormat.getImageableY());
mgr.setDoubleBufferingEnabled(false); // only for swing components
component.paint(g2d);
mgr.setDoubleBufferingEnabled(true); // only for swing components
return PAGE_EXISTS;
}

// *****************************************************************************
// STATIC METHODS
// *****************************************************************************

static public void printComponent(Component com) throws PrinterException {
new ComponentPrinter(com).print();
}

} // END PUBLIC CLASS


Related Topics



Leave a reply



Submit