Fit/Scale Jcomponent to Page Being Printed

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;

}
}

Scale JComponent to fit page page

So, part of the problem, as I see it, is some the components don't want to render until they are attached to a native peer (or realised window).

There are two choices, you could simply print the live component which is already on the screen. This is a problem, because the ComponentPrintable will change the size of the component, which will affect the live component, making it somewhat unpleasant to the user.

The second option is to create a new instance of the component and place it on another JFrame. The tricky part is getting the frame to realise it self and create a native peer.

Luckily, there are a couple of methods which we know can do this, pack been one of them.

The following example is taken from the Using Text Components example, as it's a realitvly complex component.

When you click print, it makes a new instance of this component, add's it to a JFrame, packs the frame and then prints the component.

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import static java.awt.print.Printable.PAGE_EXISTS;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

public class Main {

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

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

JButton print = new JButton("Print");
print.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TextSamplerDemo demo = new TextSamplerDemo();
JFrame test = new JFrame();
test.add(demo);
test.pack();
ComponentPrintable printable = new ComponentPrintable(demo);
PrinterJob printerJob = PrinterJob.getPrinterJob();
PageFormat pf = printerJob.pageDialog(printerJob.defaultPage());
printerJob.setPrintable(printable, pf);

if (printerJob.printDialog()) {
try {
printerJob.print();
} catch (PrinterException e1) {
JOptionPane.showMessageDialog(null, "Printing failed", "Print", JOptionPane.OK_OPTION);
}
}
}
});

JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
TextSamplerDemo demo = new TextSamplerDemo();
frame.add(demo);
frame.add(print, BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}

public class ComponentPrintable extends JPanel implements Printable {

private Component comp;

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

@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return Printable.NO_SUCH_PAGE;
}

Dimension componentSize = comp.getPreferredSize();
comp.setSize(componentSize);

Dimension printSize = new Dimension();
printSize.setSize(pageFormat.getImageableWidth(), pageFormat.getImageableHeight());

double scaleFactor = getScaleFactorToFit(componentSize, printSize);

if (scaleFactor > 1d) {
scaleFactor = 1d;
}

double scaleWidth = componentSize.width * scaleFactor;
double scaleHeight = componentSize.height * scaleFactor;

Graphics2D g2 = (Graphics2D) graphics;

double x = ((pageFormat.getImageableWidth() - scaleWidth) / 2d) + pageFormat.getImageableX();
double y = ((pageFormat.getImageableHeight() - scaleHeight) / 2d) + pageFormat.getImageableY();

AffineTransform at = new AffineTransform();

at.translate(x, y);
at.scale(scaleFactor, scaleFactor);
g2.transform(at);
comp.printAll(g2);
g2.dispose();
comp.revalidate();

return PAGE_EXISTS;
}

private 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;
}

private 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;
}
}

public class TextSamplerDemo extends JPanel
implements ActionListener {

private String newline = "\n";
protected static final String textFieldString = "JTextField";
protected static final String passwordFieldString = "JPasswordField";
protected static final String ftfString = "JFormattedTextField";
protected static final String buttonString = "JButton";

protected JLabel actionLabel;

public TextSamplerDemo() {
setLayout(new BorderLayout());

//Create a regular text field.
JTextField textField = new JTextField(10);
textField.setActionCommand(textFieldString);
textField.addActionListener(this);

//Create a password field.
JPasswordField passwordField = new JPasswordField(10);
passwordField.setActionCommand(passwordFieldString);
passwordField.addActionListener(this);

//Create a formatted text field.
JFormattedTextField ftf = new JFormattedTextField(
java.util.Calendar.getInstance().getTime());
ftf.setActionCommand(textFieldString);
ftf.addActionListener(this);

//Create some labels for the fields.
JLabel textFieldLabel = new JLabel(textFieldString + ": ");
textFieldLabel.setLabelFor(textField);
JLabel passwordFieldLabel = new JLabel(passwordFieldString + ": ");
passwordFieldLabel.setLabelFor(passwordField);
JLabel ftfLabel = new JLabel(ftfString + ": ");
ftfLabel.setLabelFor(ftf);

//Create a label to put messages during an action event.
actionLabel = new JLabel("Type text in a field and press Enter.");
actionLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));

//Lay out the text controls and the labels.
JPanel textControlsPane = new JPanel();
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();

textControlsPane.setLayout(gridbag);

JLabel[] labels = {textFieldLabel, passwordFieldLabel, ftfLabel};
JTextField[] textFields = {textField, passwordField, ftf};
addLabelTextRows(labels, textFields, gridbag, textControlsPane);

c.gridwidth = GridBagConstraints.REMAINDER; //last
c.anchor = GridBagConstraints.WEST;
c.weightx = 1.0;
textControlsPane.add(actionLabel, c);
textControlsPane.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Text Fields"),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));

//Create a text area.
JTextArea textArea = new JTextArea(
"This is an editable JTextArea. "
+ "A text area is a \"plain\" text component, "
+ "which means that although it can display text "
+ "in any font, all of the text is in the same font."
);
textArea.setFont(new Font("Serif", Font.ITALIC, 16));
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
JScrollPane areaScrollPane = new JScrollPane(textArea);
areaScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
areaScrollPane.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Plain Text"),
BorderFactory.createEmptyBorder(5, 5, 5, 5)),
areaScrollPane.getBorder()));

//Create an editor pane.
JEditorPane editorPane = createEditorPane();
JScrollPane editorScrollPane = new JScrollPane(editorPane);
editorScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
editorScrollPane.setPreferredSize(new Dimension(250, 145));
editorScrollPane.setMinimumSize(new Dimension(10, 10));

//Create a text pane.
JTextPane textPane = createTextPane();
JScrollPane paneScrollPane = new JScrollPane(textPane);
paneScrollPane.setVerticalScrollBarPolicy(
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
paneScrollPane.setPreferredSize(new Dimension(250, 155));
paneScrollPane.setMinimumSize(new Dimension(10, 10));

//Put the editor pane and the text pane in a split pane.
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
editorScrollPane,
paneScrollPane);
splitPane.setOneTouchExpandable(true);
splitPane.setResizeWeight(0.5);
JPanel rightPane = new JPanel(new GridLayout(1, 0));
rightPane.add(splitPane);
rightPane.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createTitledBorder("Styled Text"),
BorderFactory.createEmptyBorder(5, 5, 5, 5)));

//Put everything together.
JPanel leftPane = new JPanel(new BorderLayout());
leftPane.add(textControlsPane,
BorderLayout.PAGE_START);
leftPane.add(areaScrollPane,
BorderLayout.CENTER);

add(leftPane, BorderLayout.LINE_START);
add(rightPane, BorderLayout.LINE_END);
}

private void addLabelTextRows(JLabel[] labels,
JTextField[] textFields,
GridBagLayout gridbag,
Container container) {
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.EAST;
int numLabels = labels.length;

for (int i = 0; i < numLabels; i++) {
c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
c.fill = GridBagConstraints.NONE; //reset to default
c.weightx = 0.0; //reset to default
container.add(labels[i], c);

c.gridwidth = GridBagConstraints.REMAINDER; //end row
c.fill = GridBagConstraints.HORIZONTAL;
c.weightx = 1.0;
container.add(textFields[i], c);
}
}

public void actionPerformed(ActionEvent e) {
String prefix = "You typed \"";
if (textFieldString.equals(e.getActionCommand())) {
JTextField source = (JTextField) e.getSource();
actionLabel.setText(prefix + source.getText() + "\"");
} else if (passwordFieldString.equals(e.getActionCommand())) {
JPasswordField source = (JPasswordField) e.getSource();
actionLabel.setText(prefix + new String(source.getPassword())
+ "\"");
} else if (buttonString.equals(e.getActionCommand())) {
Toolkit.getDefaultToolkit().beep();
}
}

private JEditorPane createEditorPane() {
JEditorPane editorPane = new JEditorPane();
editorPane.setEditable(false);
java.net.URL helpURL = TextSamplerDemo.class.getResource(
"TextSamplerDemoHelp.html");
if (helpURL != null) {
try {
editorPane.setPage(helpURL);
} catch (IOException e) {
System.err.println("Attempted to read a bad URL: " + helpURL);
}
} else {
System.err.println("Couldn't find file: TextSampleDemoHelp.html");
}

return editorPane;
}

private JTextPane createTextPane() {
String[] initString
= {"This is an editable JTextPane, ", //regular
"another ", //italic
"styled ", //bold
"text ", //small
"component, ", //large
"which supports embedded components..." + newline,//regular
" " + newline, //button
"...and embedded icons..." + newline, //regular
" ", //icon
newline + "JTextPane is a subclass of JEditorPane that "
+ "uses a StyledEditorKit and StyledDocument, and provides "
+ "cover methods for interacting with those objects."
};

String[] initStyles
= {"regular", "italic", "bold", "small", "large",
"regular", "button", "regular", "icon",
"regular"
};

JTextPane textPane = new JTextPane();
StyledDocument doc = textPane.getStyledDocument();
addStylesToDocument(doc);

try {
for (int i = 0; i < initString.length; i++) {
doc.insertString(doc.getLength(), initString[i],
doc.getStyle(initStyles[i]));
}
} catch (BadLocationException ble) {
System.err.println("Couldn't insert initial text into text pane.");
}

return textPane;
}

protected void addStylesToDocument(StyledDocument doc) {
//Initialize some styles.
Style def = StyleContext.getDefaultStyleContext().
getStyle(StyleContext.DEFAULT_STYLE);

Style regular = doc.addStyle("regular", def);
StyleConstants.setFontFamily(def, "SansSerif");

Style s = doc.addStyle("italic", regular);
StyleConstants.setItalic(s, true);

s = doc.addStyle("bold", regular);
StyleConstants.setBold(s, true);

s = doc.addStyle("small", regular);
StyleConstants.setFontSize(s, 10);

s = doc.addStyle("large", regular);
StyleConstants.setFontSize(s, 16);

s = doc.addStyle("icon", regular);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
ImageIcon pigIcon = createImageIcon("images/Pig.gif",
"a cute pig");
if (pigIcon != null) {
StyleConstants.setIcon(s, pigIcon);
}

s = doc.addStyle("button", regular);
StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
ImageIcon soundIcon = createImageIcon("images/sound.gif",
"sound icon");
JButton button = new JButton();
if (soundIcon != null) {
button.setIcon(soundIcon);
} else {
button.setText("BEEP");
}
button.setCursor(Cursor.getDefaultCursor());
button.setMargin(new Insets(0, 0, 0, 0));
button.setActionCommand(buttonString);
button.addActionListener(this);
StyleConstants.setComponent(s, button);
}

protected ImageIcon createImageIcon(String path,
String description) {
java.net.URL imgURL = TextSamplerDemo.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL, description);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
}
}

For this to work realistically, you would need to make a copy of all the data which the panel contained originally, otherwise it's kind of pointless

How to fit width of a page when printing in java

You want to scale the Graphics2D:

g2d.translate(0, -yOffset);
double width = pageFormat.getImageableWidth();
double scale = Math.min(width / component.getWidth(),
height / component.getHeight());
if (scale < 1) {
AffineTransform oldTransform = g2d.getTransform();
g2d.scale(scale, scale);
component.printAll(g2d);
g2d.setTransform(oldTransform);
} else {
component.printAll(g2d);
}
g2d.translate(0, +yOffset);

I'm not sure what you mean by "I don't want to fit height" but you can always ignore the height, if you want:

g2d.translate(0, -yOffset);
double width = pageFormat.getImageableWidth();
double scale = width / component.getWidth();
if (scale < 1) {
AffineTransform oldTransform = g2d.getTransform();
g2d.scale(scale, scale);
component.printAll(g2d);
g2d.setTransform(oldTransform);
} else {
component.printAll(g2d);
}
g2d.translate(0, +yOffset);

Setting print size of a jLabel and put a jRadiobutton on the print

So, based on the concept presented in Printing a JFrame and its components, I've been able to produce these two examples...

NormalFilled

Which used the following JPanel as the base component...

public static class PrintForm extends JPanel {

public PrintForm() {
setLayout(new GridBagLayout());
JLabel label = new JLabel("This is a label");
label.setVerticalTextPosition(JLabel.BOTTOM);
label.setHorizontalTextPosition(JLabel.CENTER);
try {
label.setIcon(new ImageIcon(ImageIO.read(new File("C:\\hold\\thumbnails\\_cg_1009___Afraid___by_Serena_Clearwater.png"))));
} catch (IOException ex) {
ex.printStackTrace();
}
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1;
gbc.weighty = 1;
add(label, gbc);

JRadioButton rb = new JRadioButton("Open to suggestions");
rb.setSelected(true);
gbc.gridy++;
gbc.weightx = 1;
gbc.fill = GridBagConstraints.NONE;
add(rb, gbc);
}

}

And based on the concept presented in Fit/Scale JComponent to page being printed I was able to take an image of 7680x4800 and scale it down to print within an area of 842x598.

Now note. JLabel DOES NOT support scaling. If your image does not fit within the available space, you are going to have to scale it yourself some how. The following solution scales the entire component, having said that, with a little bit of clever re-arranging, it would be possible to make the TestPane scale it's image instead and use the above example instead...

Scaled

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Paper;
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.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 ScalablePrintingTest {

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


Related Topics



Leave a reply



Submit