I'm trying to get the PageFormat correct when I print. Below is an example program that shows my dilemma: I get a different result when I use printJob.setPrintable(printable) than when I use printJob.setPageable(book) when I create a Book object using the default PageFormat from the Print job.
When I run it, and click "Print", then "Print using Book", I see this console output:
doPrint(false)
printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
printing on 612.000000x792.000000 paper, imageable area=588.960000x768.960000
doPrint(true)
printing on 612.000000x792.000000 paper, imageable area=468.000000x648.000000
printing on 612.000000x792.000000 paper, imageable area=468.000000x648.000000
What gives? The default page format when using Book sucks and uses 1" margin on each side of the page; the "real" page format only needs about 1/6" margin on each side.
example program here:
package com.example.printing;
import java.awt.BasicStroke;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.geom.Rectangle2D;
import java.awt.print.Book;
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 javax.swing.AbstractAction;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PrintRectangles extends JFrame
{
static final int Nrectangles = 3;
static class RectangleThingy extends JPanel implements Printable
{
#Override public int print(Graphics graphics,
PageFormat pageFormat,
int pageIndex)
throws PrinterException
{
describePageFormat(pageFormat);
if (pageIndex > 0) {
return(NO_SUCH_PAGE);
}
else {
Graphics2D g2d = (Graphics2D)graphics;
g2d.translate(pageFormat.getImageableX(),
pageFormat.getImageableY());
double w = pageFormat.getImageableWidth();
double h = pageFormat.getImageableHeight();
final int N = (Nrectangles - 1) / 2;
final double spacing = 7.2; // 1/10 inch
g2d.setStroke(new BasicStroke(0.1f));
for (int i = -N; i <= N; ++i)
{
double dx = i*spacing;
Rectangle2D r = new Rectangle2D.Double(
dx, dx, w-2*dx, h-2*dx
);
g2d.draw(r);
}
Rectangle2D rthick = new Rectangle2D.Double(
0, 0, w, h
);
g2d.setStroke(new BasicStroke(1.0f));
g2d.draw(rthick);
return(PAGE_EXISTS);
}
}
private void describePageFormat(PageFormat pageFormat) {
System.out.println(String.format("printing on %fx%f paper, imageable area=%fx%f",
pageFormat.getWidth(),
pageFormat.getHeight(),
pageFormat.getImageableWidth(),
pageFormat.getImageableHeight()
));
}
}
static private class PrintPreviewPanel extends JPanel
{
final private Printable p;
final private PageFormat pageFormat;
public PrintPreviewPanel(Printable p, PageFormat pf)
{
this.p = p;
this.pageFormat = pf;
}
#Override public Dimension getPreferredSize() {
return new Dimension((int)this.pageFormat.getWidth(),
(int)this.pageFormat.getHeight());
}
#Override protected void paintComponent(Graphics g) {
super.paintComponent(g);
try {
p.print(g, this.pageFormat, 0);
} catch (PrinterException e) {
e.printStackTrace();
}
}
}
public PrintRectangles(String title) {
super(title);
JPanel panel = new JPanel();
setContentPane(panel);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
JButton printButton = new JButton("Print");
JButton printUsingBookButton = new JButton("Print using Book");
JButton printPreviewButton = new JButton("Print preview");
panel.add(printButton);
panel.add(printUsingBookButton);
panel.add(printPreviewButton);
printButton.addActionListener(new AbstractAction("print") {
#Override public void actionPerformed(ActionEvent e) {
doPrint(false);
}
});
printUsingBookButton.addActionListener(new AbstractAction("printUsingBook") {
#Override public void actionPerformed(ActionEvent e) {
doPrint(true);
}
});
printPreviewButton.addActionListener(new AbstractAction("printPreview") {
#Override public void actionPerformed(ActionEvent e) {
doPrintPreview();
}
});
}
protected void doPrint(boolean useBook) {
RectangleThingy rectangleThingy = new RectangleThingy();
System.out.println("doPrint("+useBook+")");
try
{
PrinterJob printJob = PrinterJob.getPrinterJob();
PageFormat pageFormat = printJob.getPageFormat(null);
if (useBook)
{
Book book = new Book();
book.append(rectangleThingy, pageFormat);
printJob.setPageable(book);
}
else
{
printJob.setPrintable(rectangleThingy);
}
if (printJob.printDialog())
printJob.print();
}
catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
}
protected void doPrintPreview() {
RectangleThingy rt = new RectangleThingy();
JFrame frame = new JFrame("print preview");
// hack for now -- how do we get this from the printer?
Paper paper = new Paper();
double dotsperinch = 72;
double margin = 0.125*dotsperinch;
double w = 8.5*dotsperinch;
double h = 11*dotsperinch;
paper.setImageableArea(margin, margin, w-2*margin, h-2*margin);
paper.setSize(w, h);
PageFormat pfmt = new PageFormat();
pfmt.setPaper(paper);
frame.setContentPane(new PrintPreviewPanel(rt, pfmt));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new PrintRectangles("PrintRectangles").start();
}
private void start() {
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
}
}
Hmm. After trying a number of fruitless efforts, it looks like setting a page to zero margin and then calling PrinterJob.validatePage() seems to be the only way I can get a valid minimum-margin PageFormat:
static private PageFormat getMinimumMarginPageFormat(PrinterJob printJob) {
PageFormat pf0 = printJob.defaultPage();
PageFormat pf1 = (PageFormat) pf0.clone();
Paper p = pf0.getPaper();
p.setImageableArea(0, 0,pf0.getWidth(), pf0.getHeight());
pf1.setPaper(p);
PageFormat pf2 = printJob.validatePage(pf1);
return pf2;
}
and then I can change doPrint() to:
protected void doPrint(boolean useBook) {
RectangleThingy rectangleThingy = new RectangleThingy();
System.out.println("doPrint("+useBook+")");
try
{
PrinterJob printJob = PrinterJob.getPrinterJob();
if (useBook)
{
Book book = new Book();
book.append(rectangleThingy, getMinimumMarginPageFormat(printJob));
printJob.setPageable(book);
}
else
{
printJob.setPrintable(rectangleThingy);
}
if (printJob.printDialog())
printJob.print();
}
catch(PrinterException pe) {
System.out.println("Error printing: " + pe);
}
}
Related
I have a scrollable JPanel, with some number drawn through the "drawString" method. When I click the zoom in button, the repaint method is invoked and the number becomes larger, as expected. But when I try to scroll to the right, part of the number is either missing or is painted in its original size instead of the new zoomed size. Below is the minimal reproducible code.
package sometest;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
public class SomeTest implements Runnable
{
public static JButton in;
public static JButton out;
public static void main(String[] args)
{
SomeTest st = new SomeTest();
st.run();
}
#Override
public void run()
{
JMenuBar mb = new JMenuBar();
in = new JButton("Zoom In");
out = new JButton("Zoom Out");
JFrame f = new JFrame("Example");
Visualization v = new Visualization();
JScrollPane sp = new JScrollPane(v);
MouseAdapter mouseAdapter = new MouseAdapter()
{
private Point origin;
#Override
public void mousePressed(MouseEvent e)
{
origin = new Point(e.getPoint());
}
#Override
public void mouseDragged(MouseEvent e)
{
JViewport vp = null;
if (origin != null)
{
vp = sp.getViewport();
}
if (vp != null)
{
int deltaX = origin.x - e.getX();
int deltaY = origin.y - e.getY();
Rectangle view = vp.getViewRect();
view.x += deltaX;
view.y += deltaY;
v.scrollRectToVisible(view);
}
}
};
sp.getViewport().addMouseListener(mouseAdapter);
sp.getViewport().addMouseMotionListener(mouseAdapter);
f.add(sp);
f.setJMenuBar(mb);
mb.add(in);
mb.add(out);
f.setContentPane(sp);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
package sometest;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
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 javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingUtilities;
import static sometest.SomeTest.in;
import static sometest.SomeTest.out;
public class Visualization extends JPanel implements Scrollable, ActionListener
{
private double zoomFactor = 1;
private double prevZoomFactor = 1;
private boolean zoomer;
private double xOffset = 0;
private double yOffset = 0;
public Visualization()
{
in.addActionListener(this);
out.addActionListener(this);
Font currentFont = getFont();
Font newFont = currentFont.deriveFont(currentFont.getSize() * 18F);
setFont(newFont);
setBackground(Color.WHITE);
}
#Override
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==in)
{
zoomer = true;
zoomFactor *= 1.1;
repaint();
}
if(e.getSource()==out)
{
zoomer = true;
zoomFactor /= 1.1;
repaint();
}
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if(zoomer)
{
AffineTransform at = new AffineTransform();
Dimension center = getPreferredScrollableViewportSize();
Double xRel = center.getWidth()/2;
Double yRel = center.getHeight()/2;
double zoomDiv = zoomFactor / prevZoomFactor;
xOffset = (zoomDiv) * (xOffset) + (1 - zoomDiv) * xRel;
yOffset = (zoomDiv) * (yOffset) + (1 - zoomDiv) * yRel;
at.translate(xOffset, yOffset);
at.scale(zoomFactor, zoomFactor);
prevZoomFactor = zoomFactor;
g2d.transform(at);
zoomer = false;
}
g2d.drawString("1234567890", 500, 200);
g2d.dispose();
}
#Override
public Dimension getPreferredSize()
{
if(zoomer)
{
return new Dimension((int)(2000*zoomFactor), (int)(750*zoomFactor));
}
else
{
return new Dimension(2000, 750);
}
}
#Override
public Dimension getPreferredScrollableViewportSize()
{
return new Dimension(1550, 750);
}
#Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)
{
return 32;
}
#Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
{
return 32;
}
#Override
public boolean getScrollableTracksViewportWidth()
{
return false;
}
#Override
public boolean getScrollableTracksViewportHeight()
{
return false;
}
}
When I zoom in and scroll, I'm expecting the drawn number to remain the same size. As far as I know, the repaint method is supposed to automatically handle cases such as when scrolling or resizing. Have I misused the repaint method or is my problem something entirely different?
It might be better to use the AffineTransform#concatenate(AffineTransform) method to combine the AffineTransform for zoom and the AffineTransform for translation.
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform scrollTransform = g2d.getTransform();
scrollTransform.concatenate(zoomTransform);
g2d.setTransform(scrollTransform);
g2d.drawString("1234567890", 500, 200);
g2d.dispose();
}
SomeTest2.java
// package sometest;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;
public class SomeTest2 {
public JComponent makeUI() {
Visualization v = new Visualization();
MouseAdapter mouseAdapter = new DragScrollListener();
v.addMouseListener(mouseAdapter);
v.addMouseMotionListener(mouseAdapter);
JButton in = new JButton("Zoom In");
in.addActionListener(e -> v.setZoomFactor(1.1));
JButton out = new JButton("Zoom Out");
out.addActionListener(e -> v.setZoomFactor(1 / 1.1));
JMenuBar mb = new JMenuBar();
mb.add(in);
mb.add(out);
EventQueue.invokeLater(() -> v.getRootPane().setJMenuBar(mb));
return new JScrollPane(v);
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new SomeTest2().makeUI());
f.setSize(640, 480);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class Visualization extends JPanel {
private final AffineTransform zoomTransform = new AffineTransform();
private final Rectangle rect = new Rectangle(2000, 750);
public Visualization() {
Font currentFont = getFont();
Font newFont = currentFont.deriveFont(currentFont.getSize() * 18F);
setFont(newFont);
setBackground(Color.WHITE);
}
public void setZoomFactor(double zoomFactor) {
zoomTransform.scale(zoomFactor, zoomFactor);
revalidate();
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform scrollTransform = g2d.getTransform();
scrollTransform.concatenate(zoomTransform);
g2d.setTransform(scrollTransform);
g2d.drawString("1234567890", 500, 200);
g2d.dispose();
}
#Override
public Dimension getPreferredSize() {
Rectangle r = zoomTransform.createTransformedShape(rect).getBounds();
return new Dimension(r.width, r.height);
}
}
class DragScrollListener extends MouseAdapter {
private final Point origin = new Point();
#Override
public void mouseDragged(MouseEvent e) {
Component c = e.getComponent();
Container p = SwingUtilities.getUnwrappedParent(c);
if (p instanceof JViewport) {
JViewport viewport = (JViewport) p;
Point cp = SwingUtilities.convertPoint(c, e.getPoint(), viewport);
Point vp = viewport.getViewPosition();
vp.translate(origin.x - cp.x, origin.y - cp.y);
((JComponent) c).scrollRectToVisible(new Rectangle(vp, viewport.getSize()));
origin.setLocation(cp);
}
}
#Override
public void mousePressed(MouseEvent e) {
Component c = e.getComponent();
Container p = SwingUtilities.getUnwrappedParent(c);
if (p instanceof JViewport) {
JViewport viewport = (JViewport) p;
Point cp = SwingUtilities.convertPoint(c, e.getPoint(), viewport);
origin.setLocation(cp);
}
}
}
I'm using Barcode4J library to generate barcodes. I am returned a BufferedImage that I can display as an ImageIcon on a JLabel and save to a file but I can't for the life of me print it to a printer, the drawString and drawRect work as expected but the image is blank. I've posted some code below; I don't know how to make it a SSCCE of this but if anyone can point me in any direction I would be most grateful.
snippet:
import org.krysalis.barcode4j.impl.code128.Code128Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import org.krysalis.barcode4j.tools.UnitConv;
public class BarcodeBean extends Code128Bean implements Printable{
BufferedImage _bImage;
public void createBarcode2Print(BarcodeBean bean, String barcodeValue) throws FileNotFoundException, IOException {
final int dpi = 150;
BufferedImage image;
BitmapCanvasProvider canvas = null;
if (barcodeValue == null) {
barcodeValue = "0123456789-000-0001";
}
bean.setModuleWidth(UnitConv.in2mm(2.0f / dpi));
bean.doQuietZone(false);
try {
canvas = new BitmapCanvasProvider(
dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
//Generate the barcode
bean.generateBarcode(canvas, barcodeValue);
image = canvas.getBufferedImage();
//Signal end of generation
// canvas.finish();
} finally {
canvas.finish();
}
printImage(image);
int stop = 0;
}
public void printImage(BufferedImage arg_image){
_bImage = arg_image;
createLabel();
}
private void createLabel() {
PrinterJob printJob = PrinterJob.getPrinterJob();
Book book = new Book();
PageFormat format = new PageFormat();
format.setOrientation(PageFormat.PORTRAIT);
java.awt.print.Paper paper = new java.awt.print.Paper();
paper.setSize(612.0, 792.0); //portrait
double hgt = paper.getHeight();
double wdth = paper.getWidth();
paper.setImageableArea(0, 0, wdth, hgt);
format.setPaper(paper);
book.append(this, format);
printJob.setPageable(book);
if (printJob.printDialog()) {
try {
printJob.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D graphics2d = (Graphics2D)graphics;
graphics2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
graphics.setColor(Color.black);
graphics2d.setColor(Color.black);
int imageWidth = _bImage.getWidth(null); // 378
int imageHeight = _bImage.getHeight(null); // 110
int imageableWidth = (int)pageFormat.getImageableWidth(); // 612
int imageableHeight = (int)pageFormat.getImageableHeight(); // 792
graphics2d.drawString("Print barcode between this and...", 10, 30);
graphics2d.drawRect(10, 40, imageWidth, imageHeight);
graphics2d.drawImage(_bImage, 10, 60, imageWidth, imageHeight, null);
graphics2d.drawString("This.............................", 10, 180);
graphics2d.dispose();
return Printable.PAGE_EXISTS;
}
}
I can't say I know why, it's possible that the Barcode4J is using their own implementation of BufferedImage or something, but, if I make a copy of the BufferedImage returned from Barcode4J, I can make it work.
What I mean by that is, if I create a new instance of BufferedImage and draw the instance from Barcode4J onto it and use it, I can make it work...
canvas = new BitmapCanvasProvider(
dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
bean.generateBarcode(canvas, barcodeValue);
BufferedImage bardcode = canvas.getBufferedImage();
image = new BufferedImage(bardcode.getWidth(), bardcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.drawImage(bardcode, 0, 0, null);
g2d.dispose();
graphics2d.dispose(); ← That might be the problem. Never dispose of a Graphics unless you created it.
👆 is really good advice! Unfortunately, in this case it didn't help, but you should listen to it anyway!
Also, this return Printable.PAGE_EXISTS;, could have you running around in circles. You need to tell the caller when there are no more pages to be printed.
Runnable example...
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.FileNotFoundException;
import java.io.IOException;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import org.krysalis.barcode4j.impl.code128.Code128Bean;
import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider;
import org.krysalis.barcode4j.tools.UnitConv;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage barcodeImage;
public TestPane() throws IOException {
setLayout(new GridBagLayout());
barcodeImage = createBarcode2Print(new Code128Bean(), "0123456789-000-0001");
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
add(new JLabel(new ImageIcon(barcodeImage)), gbc);
JButton print = new JButton("Print");
add(print, gbc);
print.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
printIt();
}
});
}
protected void printIt() {
PrinterJob pj = PrinterJob.getPrinterJob();
if (pj.printDialog()) {
PageFormat pf = pj.defaultPage();
Paper paper = pf.getPaper();
pf.setOrientation(PageFormat.PORTRAIT);
pf.setPaper(paper);
PageFormat validatePage = pj.validatePage(pf);
// System.out.println("Valid- " + dump(validatePage));
pj.setPrintable(new BarCodePrintable(barcodeImage), validatePage);
try {
pj.print();
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
}
}
public class BarCodePrintable implements Printable {
private BufferedImage barcodeImage;
public BarCodePrintable(BufferedImage barcodeImage) {
this.barcodeImage = barcodeImage;
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
Graphics2D graphics2d = (Graphics2D) graphics;
graphics2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
graphics.setColor(Color.black);
graphics2d.setColor(Color.black);
int imageWidth = barcodeImage.getWidth(); // 378
int imageHeight = barcodeImage.getHeight(); // 110
int imageableWidth = (int) pageFormat.getImageableWidth(); // 612
int imageableHeight = (int) pageFormat.getImageableHeight(); // 792
graphics2d.drawString("Print barcode between this and...", 10, 30);
graphics2d.drawRect(10, 40, imageWidth, imageHeight);
graphics2d.drawImage(barcodeImage, 10, 60, null);
graphics2d.drawString("This.............................", 10, 180);
//graphics2d.dispose();
return pageIndex == 0 ? Printable.PAGE_EXISTS : Printable.NO_SUCH_PAGE;
}
}
public BufferedImage createBarcode2Print(Code128Bean bean, String barcodeValue) throws FileNotFoundException, IOException {
final int dpi = 300;
BufferedImage image = null;
BitmapCanvasProvider canvas = null;
if (barcodeValue == null) {
barcodeValue = "0123456789-000-0001";
}
bean.setModuleWidth(UnitConv.in2mm(2.0f / dpi));
bean.doQuietZone(false);
try {
canvas = new BitmapCanvasProvider(
dpi, BufferedImage.TYPE_BYTE_BINARY, false, 0);
bean.generateBarcode(canvas, barcodeValue);
BufferedImage bardcode = canvas.getBufferedImage();
image = new BufferedImage(bardcode.getWidth(), bardcode.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = image.createGraphics();
g2d.drawImage(bardcode, 0, 0, null);
g2d.dispose();
} finally {
canvas.finish();
}
return image;
}
}
I've made a tool which could allow user to draw on screenshot.Here is my code:
package GUI.Views;
import javax.swing.*;
import javax.swing.event.MouseInputAdapter;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
public class Canvas extends JPanel {
public static int radius = 5;
public class DrawPointListener extends MouseInputAdapter{
private void draw(Point p, int radius){
Graphics2D g2 = (Graphics2D)getGraphics();
int x = (int)p.getX() - radius/2;
int y = (int)p.getY() - radius/2;
g2.fillOval(x, y, radius, radius);
}
#Override
public void mousePressed(MouseEvent e) {
draw(e.getPoint(), radius);
}
#Override
public void mouseDragged(MouseEvent e) {
draw(e.getPoint(), radius);
}
}
private BufferedImage screenShot;
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(this.screenShot, 0, 0, null);
}
public Canvas() {
this.setVisible(true);
this.screenShot = getScreenShot();
this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
DrawPointListener drawListener = new DrawPointListener();
this.addMouseListener(drawListener);
this.addMouseMotionListener(drawListener);
}
private BufferedImage getScreenShot() {
try {
return new Robot().createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));
} catch (AWTException e) {
System.out.println("Error");
return null;
}
}
public static void main(String[] args) {
JFrame f = new JFrame();
Canvas canvas = new Canvas();
f.setUndecorated(true);
f.add(canvas);
f.setExtendedState(JFrame.MAXIMIZED_BOTH);
f.setVisible(true);
}
}
Code worked fine when you try to "draw" on the screen slowly, but when you move your mouse quickly, you would see those points are not consecutive, like this:
I think that because there is a time interval of Listener.How could I improve it?
You can't change the time interval of the listener. You are NOT guaranteed to generate an event for every pixel.
So instead of drawing a single point, you need to draw a line between the current and previous point.
Something like:
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.JPanel;
class DrawingPanel extends JPanel
{
private ArrayList<ArrayList<Point>> previous = new ArrayList<ArrayList<Point>>();
private ArrayList<Point> current = new ArrayList<Point>();
private BasicStroke basicStroke;
public DrawingPanel(int strokeSize)
{
basicStroke = new BasicStroke(strokeSize, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
MouseAdapter ma = new MouseAdapter()
{
#Override
public void mousePressed(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
}
#Override
public void mouseDragged(MouseEvent e)
{
current.add( new Point(e.getX(), e.getY()) );
repaint();
}
#Override
public void mouseReleased(MouseEvent e)
{
if (current.size() > 1)
{
previous.add( current );
}
current = new ArrayList<Point>();
}
};
addMouseMotionListener( ma );
addMouseListener( ma );
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke( basicStroke );
// Paint lines from previous drags
for (int i = 0; i < previous.size(); i++)
{
drawLines(g, previous.get(i));
}
// Paint line from current drag
drawLines(g, current);
}
private void drawLines(Graphics g, ArrayList<Point> points)
{
for (int i = 0; i < points.size() - 1; i++)
{
int x = (int) points.get(i).getX();
int y = (int) points.get(i).getY();
int x2 = (int) points.get(i + 1).getX();
int y2 = (int) points.get(i + 1).getY();
g.drawLine(x, y, x2, y2);
}
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame("Drawing Panel");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(15));
frame.setSize(400, 400);
frame.setLocationByPlatform( true );
frame.setVisible( true );
}
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater( () -> createAndShowGUI() );
/*
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
*/
}
}
I have 2 classes: TestingPanel and SnipIt.
SnipIt is used for selecting an area on the screen.
TestingPanel is the main frame, containing a button to run method Snip() and receiving the return values.
If I test the SnipIt class separately, it works. But if I create a SnipIt object and run method Snip() from TestingPanel class, it doesn't work. The GUI just freezes and doesn't respond to mouse click or drag event. I guess something block the thread handle the mouse events but I'm not sure.
I have been stuck for couple hours and still don't know what caused the issue. Please help.
TestingPanel
package Testing;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JButton;
import java.awt.BorderLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TestingPanel {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestingPanel window = new TestingPanel();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public TestingPanel() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 200, 160);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JButton btnSnip = new JButton("Snip");
btnSnip.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
getSelectionSize();
}
});
btnSnip.setBounds(47, 87, 89, 23);
frame.getContentPane().add(btnSnip);
}
private void getSelectionSize() {
int[] size = new int[4];
Thread worker = new Thread(new Runnable() {
public void run() {
SnipIt sn = new SnipIt();
sn.snip();
while(!sn.complete) {
try {
Thread.sleep(800);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
size[0] = sn.returnSize()[0];
size[1] = sn.returnSize()[1];
size[2] = sn.returnSize()[2];
size[3] = sn.returnSize()[3];
}
});
worker.start();
try {
worker.join();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println(size[0] + " " + size[1] + " " + size[2] + " " + size[3]);
}
}
SnipIt
package Testing;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class SnipIt {
private int recX = 0;
private int recY = 0;
private int recWidth = 0;
private int recHeight = 0;
public boolean complete = false;
/*
public static void main(String [] args)
{
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
SnipIt s = new SnipIt();
s.snip();
}
});
}
*/
public void snip() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame();
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new SnipItPane());
frame.setBounds(getVirtualBounds());
frame.setVisible(true);
}
#SuppressWarnings("serial")
public class SnipItPane extends JPanel {
private Point mouseAnchor;
private Point dragPoint;
private SelectionPane selectionPane;
private ControlPane controlPane;
public SnipItPane() {
setOpaque(false);
setLayout(null);
selectionPane = new SelectionPane();
controlPane = new ControlPane();
add(selectionPane);
add(controlPane);
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mouseAnchor = e.getPoint();
dragPoint = null;
selectionPane.setLocation(mouseAnchor);
selectionPane.setSize(0, 0);
controlPane.setLocation(mouseAnchor);
controlPane.setSize(0, 0);
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
int width = dragPoint.x - mouseAnchor.x;
int height = dragPoint.y - mouseAnchor.y;
int x = mouseAnchor.x;
int y = mouseAnchor.y;
if (width < 0) {
x = dragPoint.x;
width *= -1;
}
if (height < 0) {
y = dragPoint.y;
height *= -1;
}
selectionPane.setBounds(x, y, width, height);
selectionPane.revalidate();
int controlY = y + height + 5;
controlPane.setBounds(x, controlY, width, 25);
controlPane.revalidate();
repaint();
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
Area area = new Area(bounds);
area.subtract(new Area(selectionPane.getBounds()));
g2d.setColor(new Color(102, 102, 102, 80));
g2d.fill(area);
}
}
#SuppressWarnings("serial")
public class ControlPane extends JPanel {
private JButton btnClose;
public ControlPane() {
setOpaque(false);
btnClose = new JButton("Save");
setLayout(new BorderLayout());
this.add(btnClose, BorderLayout.NORTH);
btnClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
complete = true;
SwingUtilities.getWindowAncestor(ControlPane.this).dispose();
}
});
}
}
#SuppressWarnings("serial")
public class SelectionPane extends JPanel {
public SelectionPane() {
setOpaque(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
recX = getX();
recY = getY();
recWidth = getWidth();
recHeight = getHeight();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
float strokeWidth = 1.0f;
float dash1[] = {10.0f};
BasicStroke dashed =
new BasicStroke(strokeWidth,
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER,
10.0f, dash1, 0.0f);
g2d.setColor(Color.BLACK);
g2d.setStroke(dashed);
g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
public int[] returnSize() {
int[] size = new int[4];
size[0] = recX;
size[1] = recY;
size[2] = recWidth;
size[3] = recHeight;
return size;
}
}
You're running the Snipit application in a background thread and then freezing that thread with Thread.sleep and a while true block, something guaranteed to freeze the GUI. Read Lesson: Concurrency in Swing and then be sure to always run Swing applications on the single Swing event thread, and do any long running or sleeping code in a background thread.
Possible solutions to your issue:
Make the Snipit window an undecorated modal dialog. This way program flow from the calling code stops when the dialog is visible and resumes when no longer visible.
Or Make the Snipit window JFrame an instance field of the class and allow outside classes to add listeners to it so that they will be notified when it closes.
e.g.,
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class TestSnipit {
private static void createAndShowGui() {
boolean runTest = true;
if (runTest) {
TestingPanel.main(null);
} else {
SnipIt.main(null);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class TestingPanel {
private JFrame frame;
/**
* Launch the application.
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestingPanel window = new TestingPanel();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* Create the application.
*/
public TestingPanel() {
initialize();
}
/**
* Initialize the contents of the frame.
*/
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 200, 160);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
JButton btnSnip = new JButton("Snip");
btnSnip.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
getSelectionSize();
}
});
btnSnip.setBounds(47, 87, 89, 23);
frame.getContentPane().add(btnSnip);
}
private void getSelectionSize() {
int[] size = new int[4];
// !!
SnipIt sn = new SnipIt();
sn.snip(frame);
// Thread worker = new Thread(new Runnable() {
// public void run() {
// SnipIt sn = new SnipIt();
// sn.snip();
//
// while (!sn.complete) {
// try {
// Thread.sleep(800);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//
size[0] = sn.returnSize()[0];
size[1] = sn.returnSize()[1];
size[2] = sn.returnSize()[2];
size[3] = sn.returnSize()[3];
// }
// });
//
// worker.start();
//
// try {
// worker.join();
// } catch (InterruptedException e1) {
// e1.printStackTrace();
// }
System.out.println(size[0] + " " + size[1] + " " + size[2] + " " + size[3]);
}
}
class SnipIt {
private int recX = 0;
private int recY = 0;
private int recWidth = 0;
private int recHeight = 0;
public boolean complete = false;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
SnipIt s = new SnipIt();
s.snip(null); // !!
}
});
}
public void snip(Window owner) { // !!
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
// JFrame frame = new JFrame();
JDialog frame = new JDialog(owner, null, ModalityType.APPLICATION_MODAL); // !!
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); // !!
frame.setLayout(new BorderLayout());
frame.add(new SnipItPane());
frame.setBounds(getVirtualBounds());
frame.setVisible(true);
}
#SuppressWarnings("serial")
public class SnipItPane extends JPanel {
private Point mouseAnchor;
private Point dragPoint;
private SelectionPane selectionPane;
private ControlPane controlPane;
public SnipItPane() {
setOpaque(false);
setLayout(null);
selectionPane = new SelectionPane();
controlPane = new ControlPane();
add(selectionPane);
add(controlPane);
MouseAdapter adapter = new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mouseAnchor = e.getPoint();
dragPoint = null;
selectionPane.setLocation(mouseAnchor);
selectionPane.setSize(0, 0);
controlPane.setLocation(mouseAnchor);
controlPane.setSize(0, 0);
}
#Override
public void mouseDragged(MouseEvent e) {
dragPoint = e.getPoint();
int width = dragPoint.x - mouseAnchor.x;
int height = dragPoint.y - mouseAnchor.y;
int x = mouseAnchor.x;
int y = mouseAnchor.y;
if (width < 0) {
x = dragPoint.x;
width *= -1;
}
if (height < 0) {
y = dragPoint.y;
height *= -1;
}
selectionPane.setBounds(x, y, width, height);
selectionPane.revalidate();
int controlY = y + height + 5;
controlPane.setBounds(x, controlY, width, 25);
controlPane.revalidate();
repaint();
}
};
addMouseListener(adapter);
addMouseMotionListener(adapter);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
Rectangle bounds = new Rectangle(0, 0, getWidth(), getHeight());
Area area = new Area(bounds);
area.subtract(new Area(selectionPane.getBounds()));
g2d.setColor(new Color(102, 102, 102, 80));
g2d.fill(area);
}
}
#SuppressWarnings("serial")
public class ControlPane extends JPanel {
private JButton btnClose;
public ControlPane() {
setOpaque(false);
btnClose = new JButton("Save");
setLayout(new BorderLayout());
this.add(btnClose, BorderLayout.NORTH);
btnClose.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
complete = true;
SwingUtilities.getWindowAncestor(ControlPane.this).dispose();
}
});
}
}
#SuppressWarnings("serial")
public class SelectionPane extends JPanel {
public SelectionPane() {
setOpaque(false);
addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
recX = getX();
recY = getY();
recWidth = getWidth();
recHeight = getHeight();
}
});
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
float strokeWidth = 1.0f;
float dash1[] = { 10.0f };
BasicStroke dashed = new BasicStroke(strokeWidth, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);
g2d.setColor(Color.BLACK);
g2d.setStroke(dashed);
g2d.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
g2d.dispose();
}
}
public static Rectangle getVirtualBounds() {
Rectangle bounds = new Rectangle(0, 0, 0, 0);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
for (GraphicsDevice gd : lstGDs) {
bounds.add(gd.getDefaultConfiguration().getBounds());
}
return bounds;
}
public int[] returnSize() {
int[] size = new int[4];
size[0] = recX;
size[1] = recY;
size[2] = recWidth;
size[3] = recHeight;
return size;
}
}
A side issue unrelated to your initial problem is your use of null layougs. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
Ok it may simple. But can't figure it out.
I have a JPanel that contains a JTable. JTable contains few rows, Sometime more, because the table model i push into it depends on database.
However, i don't use any JScollpane that enclose my JTable. As a result, when JTable contains more and more row, parent JPanel automatically resized its height. That is working fine.
Problem is i want to print whole JPanel at a time. may be it needs several page, i don't care. I can print JTable directly with a header and footer. But in my case JPanel contains some important component like JLable. So, there is no other way to avoid printing of JPanel.
I search several online link, everywhere i found a suggestion to implements printable interface.
so i implements printable in my class and overload print....
#Override
public int print(Graphics arg0, PageFormat arg1, int arg2) throws PrinterException {
Graphics2D g2d = (Graphics2D) arg0;
g2d.translate((int) arg1.getImageableX(), (int) arg1.getImageableY());
float pageWidth = (float) arg1.getImageableWidth();
float pageHeight = (float) arg1.getImageableHeight();
float imageHeight = (float) paintPanel.getHeight();
float imageWidth = (float) paintPanel.getWidth();
float scaleFactor = Math.min((float) pageWidth / (float) imageWidth, (float) pageHeight / (float) imageHeight);
int scaledWidth = (int) (((float) imageWidth) * scaleFactor);
int scaledHeight = (int) (((float) imageHeight) * scaleFactor);
BufferedImage canvas = new BufferedImage(paintPanel.getWidth(), paintPanel.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D gg = canvas.createGraphics();
paintPanel.paint(gg);
Image img = canvas;
g2d.drawImage(img, 0, 0, scaledWidth, scaledHeight, null);
return Printable.PAGE_EXISTS;
}
Its not working.
Another problem that is my second problem. I want to place a footer JPanel in every page. So how could it possible.
Help me please. Thanks.
So based on this example which demonstrates how to print a component across multiple pages, I modified it to allow for the printing of a footer.
This example draws directly via the Graphics context, but conceptually, the process would be simple enough to paint using a supplied JComponent of some sort.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import static java.awt.print.Printable.NO_SUCH_PAGE;
import static java.awt.print.Printable.PAGE_EXISTS;
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(some image source);
} 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;
private Font footerFont;
public MultiPagePrintable(JComponent component) {
this.component = component;
footerFont = new Font("Arial", Font.BOLD, 24);
}
#Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
String name = "I be mighty!";
String page = Integer.toString(pageIndex);
FontMetrics fm = graphics.getFontMetrics(footerFont);
double footerHeight = fm.getHeight() + 4;
double height = pageFormat.getImageableHeight() - footerHeight;
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.create();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
g2d.translate(0, -yOffset);
component.printAll(g2d);
g2d.translate(0, +yOffset);
Shape footerArea = new Rectangle2D.Double(0, height, pageFormat.getImageableWidth(), footerHeight);
g2d.setColor(Color.WHITE);
g2d.fill(footerArea);
g2d.setColor(Color.RED);
g2d.draw(footerArea);
g2d.setColor(Color.BLACK);
g2d.translate(0, (pageFormat.getImageableHeight() - footerHeight));
float x = 2;
float y = (float)((footerHeight - fm.getHeight()) / 2d);
g2d.drawString(name, x, y + fm.getAscent());
x = (float)(pageFormat.getImageableWidth() - fm.stringWidth(page) - 2);
g2d.drawString(page, x, y + fm.getAscent());
g2d.dispose();
result = PAGE_EXISTS;
}
return result;
}
}
}