我正在尝试将JPanel保存为图像(Png,Jpg,无论如何),但使用Graphics2D,质量非常低。我的JPanel主要包含文字,我想在A4纸张尺寸上打印面板4次,以便填充页面。 但是当我打印图像时,文字就像褪色一样。我试图创建一个A3图像,然后将其打印到较小的A4尺寸。质量增长很少,几乎没有什么意义。
这是我用来生成图像的函数,基于此ScreenImage.Class我发现here:
public void exportToPNG(JRootPane panel){
Dimension size = panel.getSize();
BufferedImage image = new BufferedImage(
size.width, size.height * 4 /* use the same image 4 times */
, BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2.drawImage(ScreenImage.createImage(panel), 0, 0, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height * 2, size.width, size.height, null);
g2.drawImage(ScreenImage.createImage(panel), 0, size.height * 3, size.width, size.height, null);
try{
ImageIO.write(image, "png", new File("D:\\test-image.png"));
}
catch(Exception e)
{
e.printStackTrace();
}
}
这是我生成的示例图像: click the link..
即使放大一点,你也应该看到图像上的褪色效果。
有没有办法提高质量?还是专门的图书馆?
答案 0 :(得分:3)
让我们从明显的......
开始为什么这么重要? Java以72dpi渲染到屏幕,但是能够以300dpi(上下都打印,但这是个不错的数字)。这意味着,粗略地说,您将屏幕图像缩放4倍。正如here
所示,向上扩展永远不会令人愉快更好的解决方案是使用打印机DPI并将图像缩小到屏幕。
为方便起见,您可以使用类似......
的内容public static final float CM_PER_INCH = 0.393700787f;
public static float cmsToPixel(float cms, float dpi) {
return cmToInches(cms) * dpi;
}
public static float cmToInches(float cms) {
return cms * CM_PER_INCH;
}
要将cm&#39转换为给定DPI的像素。
现在到了有趣的部分。你"可能"使用Swing组件渲染基本布局,它可能更容易,但您必须缩小图形,因为核心打印机API假定DPI为72(不要问)。现在按比例缩小通常会产生更好的输出,但还有另一种解决方案。
相反,你可以使用Graphics 2D API并自己生成输出......
public static class Ticket {
public enum TextAlignment {
LEFT,
RIGHT,
CENTRE
}
protected static final int STUB_NUMBER_Y_POS = 12;
private Font plainFont;
private Font boldFont;
private Stroke dashedStroke;
public void paint(Graphics2D g2d, double pageWidth, double pageHeight, int stubNumber) {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
paintLeftStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintBody((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintRightStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
paintEndStub((Graphics2D) g2d.create(), pageWidth, pageHeight, stubNumber);
g2d = (Graphics2D) g2d.create();
g2d.setColor(Color.GRAY);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(0, pageHeight - 1, pageWidth, pageHeight - 1));
g2d.dispose();
}
protected void paintLeftStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
double stubWidth = pageWidth / 4;
Graphics2D g2d = (Graphics2D) graphics.create();
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
// Did mention I hate doing inline transformations :P
g2d.rotate(Math.toRadians(-90), stubWidth / 2, pageHeight / 2);
g2d.translate(0, -((stubWidth - pageHeight) / 2));
String lines[] = {"MORATUFIESTA", "", "Sat. 3 Auguest 2015", "Adult - $3"};
double x = 2;
double y = 0;
double maxWidth = 0;
for (String text : lines) {
x = calculateHorizontalCenterPositionFor(text, fm, stubWidth);
maxWidth = Math.max(maxWidth, fm.stringWidth(text));
g2d.drawString(text, (int) Math.round(x), (int) Math.round(y + fm.getAscent()));
y += fm.getHeight();
}
double blockWidth = y;
// Easier then trying to undo the transformation...
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Low";
font = getPlainFont().deriveFont(6f);
g2d.setFont(font);
fm = g2d.getFontMetrics();
double xPos = calculateHorizontalCenterPositionFor(text, fm, blockWidth) + 2;
double yPos = (pageHeight - maxWidth) / 2;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos - fm.getAscent()));
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, stubWidth - 8 - fm.getHeight() - fm.getAscent(), STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected Stroke getDashedStroke() {
if (dashedStroke == null) {
float dash[] = {10.0f};
dashedStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 10f, dash, 0.0f);
}
return dashedStroke;
}
public Font getPlainFont() {
if (plainFont == null) {
plainFont = UIManager.getFont("Label.font");
}
return plainFont;
}
public Font getBoldFont() {
if (boldFont == null) {
boldFont = getPlainFont();
boldFont = boldFont.deriveFont(Font.BOLD);
}
return boldFont;
}
protected double calculateHorizontalCenterPositionFor(String text, FontMetrics fm, double width) {
return (width - fm.stringWidth(text)) / 2d;
}
protected void paintBody(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
int padding = 8;
double xOffset = pageWidth / 4d;
graphics.setColor(Color.BLACK);
double bodyWidth = (pageWidth / 2d);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setFont(getPlainFont().deriveFont(12f));
FontMetrics fm = g2d.getFontMetrics();
String text = "Moratu Fiesta";
double xPos = bodyWidth - fm.stringWidth(text) - padding;
double yPos = padding;
g2d.drawString(text, (int) Math.round(xPos), (int) Math.round(yPos + fm.getAscent()));
g2d.dispose();
Font plainFont = getPlainFont().deriveFont(9.5f);
TextLine[] addressLines01 = new TextLine[]{
new TextLine("61 Railway Pde North", plainFont),
new TextLine("Glen Waverley", plainFont),
new TextLine("03 9836 8673", plainFont)
};
TextLine[] addressLines02 = new TextLine[]{
new TextLine("1120, Glen Huntly", plainFont),
new TextLine("Glen Huntly", plainFont),
new TextLine("03 9571 5544", plainFont)
};
TextLine[] sponsorLines = new TextLine[]{
new TextLine("Proudly supported by", plainFont),
new TextLine("Quality Groceries", plainFont.deriveFont(Font.BOLD)),
new TextLine("Visit for all your grocery needs", plainFont)
};
Area area = new Area();
addTo(g2d, area, addressLines01);
addTo(g2d, area, addressLines02);
addTo(g2d, area, sponsorLines);
int height = area.getBounds().height;
double bottomBlockYPos = pageHeight - height - padding;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, addressLines01);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, sponsorLines);
drawTextLines(g2d, padding, bottomBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, addressLines02);
g2d.dispose();
plainFont = getPlainFont().deriveFont(10f);
TextLine[] textLines = new TextLine[]{
new TextLine("On Saturday, August 3, 2013 from 6.30pm till midnight", plainFont.deriveFont(Font.BOLD)),
new TextLine("At 21, Sacred Heart Parish Hall, Johnson Street, Oakleigh", plainFont)
};
int blockHeight = getSizeFor(g2d, textLines).height;
double mainBlockYPos = (pageHeight - blockHeight) / 2;
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
drawTextLines(g2d, 0, mainBlockYPos, bodyWidth, TextAlignment.CENTRE, textLines);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
Font boldFont = getBoldFont().deriveFont(9f);
double upperYPos = (mainBlockYPos + blockHeight);
double middleBlockYPos = upperYPos + ((bottomBlockYPos - upperYPos) / 2) - (g2d.getFontMetrics(boldFont).getHeight() / 2);
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.LEFT, new TextLine("Melway Ref, 69 FB", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.CENTRE, new TextLine("Music by REDEMPTION", boldFont));
drawTextLines(g2d, padding, middleBlockYPos, bodyWidth - (padding * 2), TextAlignment.RIGHT, new TextLine("Donations $30", boldFont));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(bodyWidth, 0, bodyWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
}
protected void drawStubNumber(Graphics2D g2d, double x, double y, int stubNumber) {
Font font = getBoldFont().deriveFont(18f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
String text = Integer.toString(stubNumber);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintRightStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double xOffset = (pageWidth / 4d) * 3;
double stubWidth = (pageWidth / 4d) / 2;
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Cafe Little Hut";
Font font = getPlainFont().deriveFont(23f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text)) / 2d);
double y = ((pageHeight - fm.getHeight()) / 2d);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected void paintEndStub(Graphics2D graphics, double pageWidth, double pageHeight, int stubNumber) {
graphics.setColor(Color.BLACK);
int padding = 8;
double stubWidth = (pageWidth / 4d) / 2;
double xOffset = ((pageWidth / 4d) * 3 + stubWidth);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(xOffset, 0);
g2d.setStroke(getDashedStroke());
g2d.draw(new Line2D.Double(stubWidth, 0, stubWidth, pageHeight));
g2d.dispose();
g2d = (Graphics2D) graphics.create();
drawStubNumber(g2d, padding + xOffset, STUB_NUMBER_Y_POS, stubNumber);
g2d.dispose();
g2d = (Graphics2D) graphics.create();
String text = "Entrance";
Font font = getBoldFont().deriveFont(32f);
g2d.setFont(font);
FontMetrics fm = g2d.getFontMetrics();
double x = xOffset + ((stubWidth - fm.stringWidth(text)) / 2);
double y = ((pageHeight - fm.getHeight()) / 2);
g2d.translate(x, y);
g2d.rotate(Math.toRadians(-90), fm.stringWidth(text) / 2, fm.getHeight() / 2);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();
}
protected Dimension drawTextLines(Graphics2D g2d, double xPos, double yPos, double width, TextAlignment textAlignment, TextLine... textLines) {
Area area = new Area();
for (TextLine textLine : textLines) {
g2d.translate(xPos, yPos);
Dimension size = textLine.getBounds(g2d);
textLine.paint(g2d, width, textAlignment);
area.add(new Area(new Rectangle2D.Double(xPos, yPos, size.width, size.height)));
g2d.translate(-xPos, -yPos);
yPos += size.height;
}
return area.getBounds().getSize();
}
protected void addTo(Graphics2D g2d, Area area, TextLine... textLines) {
area.add(new Area(new Rectangle(getSizeFor(g2d, textLines))));
}
protected Dimension getSizeFor(Graphics2D g2d, TextLine... textLines) {
int yPos = 0;
int width = 0;
for (TextLine textLine : textLines) {
Dimension size = textLine.getBounds(g2d);
yPos += size.height;
width = Math.max(size.width, width);
}
return new Dimension(width, yPos);
}
protected class TextLine {
private String text;
private Font font;
public TextLine(String text, Font font) {
this.text = text;
this.font = font;
}
public String getText() {
return text;
}
public Font getFont() {
return font;
}
public Dimension getBounds(Graphics2D g2d) {
FontMetrics fm = g2d.getFontMetrics(getFont());
return new Dimension(fm.stringWidth(text), fm.getHeight());
}
public void paint(Graphics2D g2d, double width, TextAlignment textAlignment) {
Dimension bounds = getBounds(g2d);
FontMetrics fm = g2d.getFontMetrics(getFont());
g2d.setFont(font);
double x = 0;
switch (textAlignment) {
case CENTRE:
x = (width - bounds.width) / 2;
break;
case RIGHT:
x = width - bounds.width;
break;
}
g2d.drawString(getText(), (int) Math.round(x), fm.getAscent());
}
}
}
你为什么这样做?将Printable
包裹起来并打印到打印机更容易,您也可以“#34; paint"到组件(或图像)
例如......
public class TicketPrintable implements Printable {
private Ticket ticket;
public TicketPrintable(Ticket ticket) {
this.ticket = ticket;
}
@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
int result = NO_SUCH_PAGE;
if (pageIndex == 0) {
Graphics2D g2d = (Graphics2D) graphics;
double width = pageFormat.getImageableWidth();
double height = pageFormat.getImageableHeight();
g2d.translate((int) pageFormat.getImageableX(),
(int) pageFormat.getImageableY());
double ticketHeight = height / 4d;
for (int index = 0; index < 4; index++) {
ticket.paint(g2d, width, ticketHeight, index + 1);
g2d.translate(0, ticketHeight);
}
result = PAGE_EXISTS;
}
return result;
}
}
要打印它,你会使用像...这样的东西。
PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet();
aset.add(MediaSizeName.ISO_A4);
aset.add(new PrinterResolution(300, 300, PrinterResolution.DPI));
aset.add(new MediaPrintableArea(0, 0, 210, 297, MediaPrintableArea.MM));
PrinterJob pj = PrinterJob.getPrinterJob();
pj.setPrintable(new TicketPrintable(new Ticket()));
if (pj.printDialog(aset)) {
try {
pj.print(aset);
} catch (PrinterException ex) {
ex.printStackTrace();
}
}
在屏幕上看起来像......
在纸面上它看起来像(缩小为SO)
现在说过,我强烈建议您学习JasperReports,这使得这一切变得更加简单......
<强>更新强>
所以,我坐在交通中并想着自己,我想知道我是否可以使用缩放将Ticket
呈现给图像,所以我想我试一试......
顶部图像为72dpi,底部图像缩放为300dpi
double pageWidth = cmsToPixel(21.0f, 300f);
double pageHeight = cmsToPixel(29.7f, 300f);
double imageWidth = cmsToPixel(21.0f, 72f);
double imageHeight = cmsToPixel(29.7f, 72f);
double scaleFactor = ImageUtilities.getScaleFactorToFit(
new Dimension((int) Math.round(imageWidth), (int) Math.round(imageHeight)),
new Dimension((int) Math.round(pageWidth), (int) Math.round(pageHeight)));
int width = (int) Math.round(pageWidth);
int height = (int) Math.round(pageHeight);
BufferedImage img = new BufferedImage(
width,
height,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
g2d.setColor(Color.WHITE);
g2d.fill(new Rectangle2D.Double(0, 0, img.getWidth(), img.getHeight()));
g2d.scale(scaleFactor, scaleFactor);
Ticket ticket = new Ticket();
ticket.paint(g2d, img.getWidth() / scaleFactor, (img.getHeight() / scaleFactor) / 4, 1);
g2d.dispose();
try {
ImageIO.write(img, "png", new File("Ticket.png"));
} catch (IOException ex) {
ex.printStackTrace();
}
比例因子算法
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;
}
注意:这只适用于文本和原始图形,你将图像放入其中并且它不会工作,图像将被放大并看起来很糟糕,使用在之前的链接答案中展示。在这种情况下,您必须将表单设计为以300 + dpi渲染并将图像缩小到72dpi