I'm nervous about asking this because I'm scared I'll get told off. I am trying to print a JPanel that is about 4 pages long. It could be more, the data is from a JDBC MySql query, that could end up being many more that 4. The JPanel I am trying to print is full of other JPanels (with competitors race details on them) that I have added via a ListArray..
Anyway, I have scoured all day through stackoverflow and have found may examples of code that I have tried to implement. The printable class only prints the first page, so I have tried to implement the pageable class, but I just can't seem to get it right. I have tried to implement the Book class but don't know how to add my JPanel to the book. I have looked here, here and many more (I'm only allowed to post 2 links).
Here is my current code (that I got off one of the answers that #madprogrammer gave) -
int printButton = JOptionPane.showOptionDialog(null, scrollPane, "Race Winners", JOptionPane.YES_NO_OPTION,
JOptionPane.PLAIN_MESSAGE, // no Icon
null, //do not use a custom Icon
options, //the titles of buttons
options[0]); //default button title
if (printButton == 0) {
try {
printComponent(pane, true);
//printComponentToFile(pane, false);
} catch (PrinterException exp) {
exp.printStackTrace();
}
public static void printComponent(JComponent comp, boolean fill) throws PrinterException {
PrinterJob pjob = PrinterJob.getPrinterJob();
//pjob.setPageable(comp);
PageFormat pf = pjob.defaultPage();
pf.setOrientation(PageFormat.PORTRAIT);
PageFormat postformat = pjob.pageDialog(pf);
if (pf != postformat) {
//Set print component
//pjob.setPageable(comp);
pjob.setPrintable(new ComponentPrinter(comp, fill), postformat);
if (pjob.printDialog()) {
pjob.print();
}
}
}
public static void printComponentToFile(Component comp, boolean fill) throws PrinterException {
Paper paper = new Paper();
paper.setSize(8.3 * 72, 11.7 * 72);
paper.setImageableArea(18, 18, 559, 783);
PageFormat pf = new PageFormat();
pf.setPaper(paper);
pf.setOrientation(PageFormat.PORTRAIT);
BufferedImage img = new BufferedImage(
(int) Math.round(pf.getWidth()), (int) Math.round(pf.getHeight()),
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle(0, 0, img.getWidth(), img.getHeight()));
ComponentPrinter cp = new ComponentPrinter(comp, fill);
try {
cp.print(g2d, pf, 0);
} finally {
g2d.dispose();
}
try {
ImageIO.write(img, "png", new File("Page-" + (fill ? "Filled" : "") + ".png"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static class ComponentPrinter implements Printable, Pageable {
private Component comp;
private boolean fill;
int numPages;
PageFormat format;
public ComponentPrinter(Component comp, boolean fill) {
this.comp = comp;
this.fill = fill;
}
#Override
public int print(Graphics g, PageFormat format, int page_index) throws PrinterException {
numPages = (int) Math.ceil(comp.getHeight() / format.getImageableY());
System.out.print(numPages);
if (page_index > 0) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) g;
g2.translate(format.getImageableX(), format.getImageableY());
//g2.translate(format.getImageableX(), format.getImageableY()- page_index*comp.getPreferredSize().height);
double width = (int) Math.floor(format.getImageableWidth());
double height = (int) Math.floor(format.getImageableHeight());
if (!fill) {
width = Math.min(width, comp.getPreferredSize().width);
height = Math.min(height, comp.getPreferredSize().height);
}
comp.setBounds(0, 0, (int) Math.floor(width), (int) Math.floor(height));
if (comp.getParent() == null) {
comp.addNotify();
}
comp.validate();
comp.doLayout();
comp.printAll(g2);
if (comp.getParent() != null) {
comp.removeNotify();
}
return Printable.PAGE_EXISTS;
}
#Override
public int getNumberOfPages() {
// TODO Auto-generated method stub
return numPages;
}
#Override
public PageFormat getPageFormat(int arg0) throws IndexOutOfBoundsException {
return format;
}
#Override
public Printable getPrintable(int arg0) throws IndexOutOfBoundsException {
// TODO Auto-generated method stub
return this;
}
}
This all works, but only prints the first page. I would REALLY appreciate a nudge in the right direction with this as I'm stumped. TIA :-)
This is how you print a Component (JPanel) on multiple pages:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.print.*;
import javax.swing.RepaintManager;
public class PrintMultiPageUtil implements Printable, Pageable {
private Component componentToBePrinted;
private PageFormat format;
private int numPages;
public PrintMultiPageUtil(Component componentToBePrinted) {
this.componentToBePrinted = componentToBePrinted;
// get total space from component
Dimension totalSpace = this.componentToBePrinted.getPreferredSize();
// calculate for DIN A4
format = PrinterJob.getPrinterJob().defaultPage();
numPages = (int) Math.ceil(totalSpace .height/format.getImageableHeight());
}
public void print() {
PrinterJob printJob = PrinterJob.getPrinterJob();
// show page-dialog with default DIN A4
format = printJob.pageDialog(printJob.defaultPage());
printJob.setPrintable(this);
printJob.setPageable(this);
if (printJob.printDialog())
try {
printJob.print();
} catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
}
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if ((pageIndex < 0) | (pageIndex >= numPages)) {
return(NO_SUCH_PAGE);
} else {
Graphics2D g2d = (Graphics2D)g;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY() - pageIndex * pageFormat.getImageableHeight());
disableDoubleBuffering(componentToBePrinted);
componentToBePrinted.paint(g2d);
enableDoubleBuffering(componentToBePrinted);
return(PAGE_EXISTS);
}
}
public static void disableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(false);
}
public static void enableDoubleBuffering(Component c) {
RepaintManager currentManager = RepaintManager.currentManager(c);
currentManager.setDoubleBufferingEnabled(true);
}
#Override
public int getNumberOfPages() {
// TODO Auto-generated method stub
return numPages;
}
#Override
public PageFormat getPageFormat(int arg0) throws IndexOutOfBoundsException {
return format;
}
#Override
public Printable getPrintable(int arg0) throws IndexOutOfBoundsException {
// TODO Auto-generated method stub
return this;
}
}
numPages
I changed the expression for numPages to:
(int) Math.ceil(page.height/format.getImageableHeight())
This divides the total height (height of the jpanel) through the height of one page, thus calculating the number of all pages.
g2d.translate
I did the following change: In this line:
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY() - pageIndex * pageFormat.getImageableHeight());
Changed componentToBePrinted.getPreferredSize().height to pageFormat.getImageableHeight(). A positive value for the first or the second parameter of g2d.translate moves the graphic to the right or down respectively.
.getImageableX() and .getImageableY() help position the graphic so that it doesn't overlap with the padding.
For pageIndex greater than 0, - pageIndex * pageFormat.getImageableHeight() moves the image pageIndex-times the page-height to the top. So the area, that the pageIndex refers to is printed.
original broken source:: https://community.oracle.com
Here is my original answer.
Related
I want to print an image on thermal printer, but when I use this code, the image gets smaller. How can I change the size of the image that I'm going to print?
public void printPhoto() throws Exception {
PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
pras.add(new Copies(1));
pras.size();
PrintService pss[] = PrintServiceLookup.lookupPrintServices(DocFlavor.INPUT_STREAM.PNG, pras);
if (pss.length == 0)
throw new RuntimeException("No printer services available.");
PrintService ps = findPrintService("POS-58", pss);
System.out.println("Printing to " + ps);
DocPrintJob job = ps.createPrintJob();
FileInputStream fin = new FileInputStream("D:\\QRCODE\\test.png");
Doc doc = new SimpleDoc(fin, DocFlavor.INPUT_STREAM.PNG, null);
job.print(doc, pras);
fin.close();
}
Can anyone help me? This is for our school project. Thanks a lot.
I change my code to this
private static BufferedImage image;
public void PrintLogo(){
try {
image = ImageIO.read(new File("C:\\Users\\ADMIN\\Desktop\\logothermal.png"));
System.out.println(image.getWidth() + "x" + image.getHeight());
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
// 86X54mm
double width = fromCMToPPI(7.6);
double height = fromCMToPPI(3.4);
paper.setSize(width, height);
paper.setImageableArea(
fromCMToPPI(0.1),
fromCMToPPI(0.1),
width - fromCMToPPI(0.1),
height - fromCMToPPI(0.1));
pf.setOrientation(PageFormat.PORTRAIT);
pf.setPaper(paper);
PageFormat validatePage = pj.validatePage(pf);
System.out.println("Valid- " + dump(validatePage));
pj.setPrintable(new MyPrintable(), validatePage);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
} catch (IOException exp) {
exp.printStackTrace();
}
}
public static double fromPPItoCM(double dpi) {
return dpi / 72 / 0.393700787;
}
public static double fromCMToPPI(double cm) {
return toPPI(cm * 0.393700787);
}
public static double toPPI(double inch) {
return inch * 72d;
}
public static String dump(Paper paper) {
StringBuilder sb = new StringBuilder(64);
sb.append(paper.getWidth()).append("x").append(paper.getHeight())
.append("/").append(paper.getImageableX()).append("x").
append(paper.getImageableY()).append(" - ").append(paper
.getImageableWidth()).append("x").append(paper.getImageableHeight());
return sb.toString();
}
public static String dump(PageFormat pf) {
Paper paper = pf.getPaper();
return dump(paper);
}
public static class MyPrintable implements Printable {
#Override
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
System.out.println(pageIndex);
int result = NO_SUCH_PAGE;
if (pageIndex < 1) {
Graphics2D g2d = (Graphics2D) graphics;
System.out.println("[Print] " + dump(pageFormat));
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight()/2;
System.out.println("Print Size = " + fromPPItoCM(width) + "x" +
fromPPItoCM(height));
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
Image scaled = null;
if (width > height) {
scaled = image.getScaledInstance((int)Math.round(width), -1,
Image.SCALE_SMOOTH);
} else {
scaled = image.getScaledInstance(-1, (int)Math.round(height),
Image.SCALE_SMOOTH);
}
g2d.drawImage(scaled, 0, 0, null);
result = PAGE_EXISTS;
}
return result;
}
}
I wanted to print what is on the canvas and came across this suggested code:
//https://www.cis.upenn.edu/~bcpierce/courses/629/jdkdocs/guide/awt/designspec/printing.html
PrintJob pjob = getToolkit().getPrintJob(new Frame(), "Print TimeGraph", null);
if(pjob !=null) {
Graphics pg = pjob.getGraphics();
if (pg != null) {
canvas.printAll(pg);
pg.dispose(); // flush page
}
pjob.end();
}
Then in the canvas paint method:
#Override
public void paint(Graphics g) {
if(g instanceof PrintGraphics){
if(graphTitle != null){
g.drawString ("Hello Printer",left+10,top+50);
}
}
}
It printed but some of the margins were cut off. I read that awt.printable enabled better margin control so I changed the code to:
PrinterJob pj = PrinterJob.getPrinterJob();
PageFormat pf = pj.defaultPage();
Paper paper = new Paper();
double margin = 4.5;
paper.setImageableArea(margin, margin, paper.getWidth() - margin * 2, paper.getHeight() - margin * 2);
pf.setPaper(paper);
pj.setPrintable(new MyPrintable(), pf);
if (pj.printDialog()) {
try {
pj.print();
} catch (PrinterException pp) {
System.out.println(pp);
}
}
and
class MyPrintable implements Printable {
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0)
return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D) g;
g2.translate(pf.getImageableX(), pf.getImageableY());
canvas.printAll(g);
return PAGE_EXISTS;
}
}
And that solved the margin issue. The problem is that I no longer get an instanceof PrintGraphics in the paint event. Its always a Graphics object an there is no way to print additional information when paint is invoked from printing. I tried casting the Graphics object to a PrintGraphics object in the canvas.printAll method to no avail. How can I regain the ability to differentiate what called the paint method either by checking the object type or by some other means?
Perhaps hackish but an indirection with extra parameter, on the line of the code below, may solve your problem for the moment.
class MyCanvas {
protected void doPaint(Graphics g, boolean forPrinter) {
if(forPrinter){
if(graphTitle != null){
g.drawString ("Hello Printer",left+10,top+50);
}
}
}
#Override
public void paint(Graphics g) {
this.doPaint(g, false);
}
}
class MyPrintable implements Printable {
public int print(Graphics g, PageFormat pf, int pageIndex) {
if (pageIndex != 0)
return NO_SUCH_PAGE;
Graphics2D g2 = (Graphics2D) g;
g2.translate(pf.getImageableX(), pf.getImageableY());
// If on many pages, this will need to be handled better.
// But as are ignoring the pageIndex, I assume
// you are on a single page and pleased to print everything on it
canvas.doPaint(g, true);
return PAGE_EXISTS;
}
}
PS: unless you really want to have a "composite printable" it is much better to make you MyCanvas implement the Printable interface itself.This implies: move the public int print(Graphics g, PageFormat pf, int pageIndex) inside the MyCanvas class itself - cannot hurt and it helps if you want to print something you compute using the MyCanvas non-public members.
I would like to know if there is a proper way of printing a BufferedImage in Java.
Basically I have created a photo manipulation program which works fine, I can save images etc.
But my real goal is to send it to the printer software so that you can select the amount of pages you want to print and page type.
So my shortened question is, how do I send a buffered image to the printer so that a printer selection screen will popup etc and then be able to print?
If anyone can show me an example of this, it would be greatly appreciated.
Here's one from one of my Java projects. This code will scale and print one image on a printer page.
You call it like this:
printButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
//Start a Thread
new Thread(new PrintActionListener(image)).start();
}
});
Here's the Runnable:
import java.awt.Graphics;
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;
public class PrintActionListener implements Runnable {
private BufferedImage image;
public PrintActionListener(BufferedImage image) {
this.image = image;
}
#Override
public void run() {
PrinterJob printJob = PrinterJob.getPrinterJob();
printJob.setPrintable(new ImagePrintable(printJob, image));
if (printJob.printDialog()) {
try {
printJob.print();
} catch (PrinterException prt) {
prt.printStackTrace();
}
}
}
public class ImagePrintable implements Printable {
private double x, y, width;
private int orientation;
private BufferedImage image;
public ImagePrintable(PrinterJob printJob, BufferedImage image) {
PageFormat pageFormat = printJob.defaultPage();
this.x = pageFormat.getImageableX();
this.y = pageFormat.getImageableY();
this.width = pageFormat.getImageableWidth();
this.orientation = pageFormat.getOrientation();
this.image = image;
}
#Override
public int print(Graphics g, PageFormat pageFormat, int pageIndex)
throws PrinterException {
if (pageIndex == 0) {
int pWidth = 0;
int pHeight = 0;
if (orientation == PageFormat.PORTRAIT) {
pWidth = (int) Math.min(width, (double) image.getWidth());
pHeight = pWidth * image.getHeight() / image.getWidth();
} else {
pHeight = (int) Math.min(width, (double) image.getHeight());
pWidth = pHeight * image.getWidth() / image.getHeight();
}
g.drawImage(image, (int) x, (int) y, pWidth, pHeight, null);
return PAGE_EXISTS;
} else {
return NO_SUCH_PAGE;
}
}
}
}
I want to print a JTable with background image or water mark. My code is:
public void actionPerformed(ActionEvent ae)
{
boolean status=false;
MessageFormat header = null;
header = new MessageFormat("Header");
MessageFormat footer = null;
footer = new MessageFormat("Page");
boolean fitWidth = true;
boolean showPrintDialog = true;
boolean interactive = true;
/* determine the print mode */
JTable.PrintMode mode = fitWidth ? JTable.PrintMode.FIT_WIDTH
: JTable.PrintMode.NORMAL;
try
{
status = jt.print(mode, header, footer,showPrintDialog,null,interactive);
if(status ==true)
{
frame.dispose();
}
}
catch(Exception ee)
{
System.out.println(ee.getMessage());
}
}
How can I pass or set the background image in this method?
there no easy way to set whatever for BackGround for whole JTable, but JViewPort from JScrollPane can do that easilly, then doesn't matter if is inside JScrollPane a JTable or another JComponents
for example
import java.awt.*;
import javax.swing.*;
class ImageAsTableBackround {
private JScrollPane sp;
private JTable table;
private String[] head = {"One", "Two", "Three", "Four", "Five", "Six"};
private String[][] data = new String[25][6];
public void buildGUI() {
sp = new JScrollPane();
// uncomment these codes lines for panting an image from package,
// but then block code table = new TableBackroundPaint0(data, head);
//sp.setViewport(new ImageViewport());
//table = new JTable(data, head);
//table.setOpaque(false);
// code for painting from generated code
table = new TableBackroundPaint0(data, head);
table.setBackground(new Color(0, 0, 0, 0));
table.setFillsViewportHeight(true);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
sp.setViewportView(table);
JFrame frame = new JFrame();
frame.getContentPane().add(sp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class ImageViewport extends JViewport {
private static final long serialVersionUID = 1L;
private Image img;
public ImageViewport() {
try {
ImageIcon image = new ImageIcon(getClass().getResource("resources/PICT6090.jpg"));
img = image.getImage();
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this.getWidth(), this.getHeight(), this);
} else {
g.drawString("This space for rent", 50, 50);
}
}
}
class TableBackroundPaint0 extends JTable {
private static final long serialVersionUID = 1L;
TableBackroundPaint0(Object[][] data, Object[] head) {
super(data, head);
setOpaque(false);
((JComponent) getDefaultRenderer(Object.class)).setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
Color background = new Color(168, 210, 241);
Color controlColor = new Color(230, 240, 230);
int width = getWidth();
int height = getHeight();
Graphics2D g2 = (Graphics2D) g;
Paint oldPaint = g2.getPaint();
g2.setPaint(new GradientPaint(0, 0, background, width, 0, controlColor));
g2.fillRect(0, 0, width, height);
g2.setPaint(oldPaint);
for (int row : getSelectedRows()) {
Rectangle start = getCellRect(row, 0, true);
Rectangle end = getCellRect(row, getColumnCount() - 1, true);
g2.setPaint(new GradientPaint(start.x, 0, controlColor, (int) ((end.x + end.width - start.x) * 1.25), 0, Color.orange));
g2.fillRect(start.x, start.y, end.x + end.width - start.x, start.height);
}
super.paintComponent(g);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new ImageAsTableBackround().buildGUI();
}
});
}
}
The problem has two parts
print an image an each page
be sure the image "shines-through" the table
the second is addressed by #mKorbel (though not really solved because there is no nice solution :-)
To solve the first, I would go for a custom Printable and subclass JTable to return it, something like
public class BackgroundPrintable implements Printable {
Printable tablePrintable;
JTable table;
MessageFormat header;
MessageFormat footer;
BufferedImage background;
public BackgroundPrintable(MessageFormat header, MessageFormat footer) {
this.header = header;
this.footer = footer;
}
public void setTablePrintable(JTable table, Printable printable) {
tablePrintable = printable;
this.table = table;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
printImage(graphics, pageFormat, pageIndex);
int exists = tablePrintable.print(graphics, pageFormat, pageIndex);
if (exists != PAGE_EXISTS) {
return exists;
}
return PAGE_EXISTS;
}
private void printImage(Graphics graphics, PageFormat pageFormat,
int pageIndex) {
// grab an untainted graphics
Graphics2D g2d = (Graphics2D)graphics.create();
// do the image painting
....
// cleanup
g2d.dispose();
}
}
// use in JTable subclass
#Override
public Printable getPrintable(PrintMode printMode,
MessageFormat headerFormat, MessageFormat footerFormat) {
Printable printable = super.getPrintable(printMode, null, null);
BackgroundPrintable custom = new BackgroundPrintable(headerFormat, footerFormat);
custom.setTablePrintable(this, printable);
return custom;
}
To achieve the second, both the JTable and its renderers must be transparent. Tricky, because:
probably only if printing - otherwise they should have their usual opacity
all of the renderers must be transparent, and there is no completely safe way to get hold of them all
A custom JTable could try to achieve that by forcing the rendering component's opacity in its prepareRenderer:
#Override
public Component prepareRenderer(TableCellRenderer renderer,
int row, int column) {
JComponent comp = (JComponent) super.prepareRenderer(renderer, row, column);
if (isPaintingForPrint()) {
comp.setOpaque(false);
} else {
comp.setOpaque(true);
}
return comp;
}
Actually, that's not entirely valid: the code in the else block might be the wrong-thing-to-do for naturally transparent components. No really reliable solution available, I'm afraid.
private void printCard() {
PrinterJob printjob = PrinterJob.getPrinterJob();
printjob.setJobName("Label");
Printable printable = new Printable() {
public int print(Graphics pg, PageFormat pf, int pageNum) {
if (pageNum > 0) {
return Printable.NO_SUCH_PAGE;
}
Dimension size = jLayeredPane2.getSize();
BufferedImage bufferedImage = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
jLayeredPane2.print(bufferedImage.getGraphics());
Graphics2D g2 = (Graphics2D) pg;
g2.translate(pf.getImageableX(), pf.getImageableY());
g2.drawImage(bufferedImage, 0, 0, (int) pf.getWidth(), (int) pf.getHeight(), null);
return Printable.PAGE_EXISTS;
}
};
Paper paper = new Paper();
paper.setImageableArea(0, 0, 153, 243);
paper.setSize(243, 154);
PageFormat format = new PageFormat();
format.setPaper(paper);
format.setOrientation(PageFormat.LANDSCAPE);
printjob.setPrintable(printable, format);
if (printjob.printDialog() == false)
return;
try {
printjob.print();
} catch (PrinterException ex) {
System.out.println("NO PAGE FOUND." + ex);
}
}
From what I see in your code, you call if (printjob.printDialog() == false). This will always try to show the native printer properties dialog. The boolean return value is based on whether the user clicks OK or cancels out of the dialog. If you want to suppress the dialog, remove that if block, as the printing work that you want to perform is done via the printjob.print() call.