将JPanel保存为图像高清质量

我试图将JPanel保存为图像(Png,Jpg,无论如何)但是使用Graphics2D时质量非常低。 我的JPanel主要包含文本,我想在A4纸张尺寸上打印面板4次,以便填充页面。 但是当我打印图像时,文字就像褪色一样。 我试图创建一个A3图像,然后将其打印到较小的A4尺寸。 质量增长很少,几乎无关紧要。

这是我用来生成图像的函数,基于这个我在这里找到的ScreenImage.Class:

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(); } } 

这是我生成的示例图像: 单击链接..

即使放大一点,你也应该看到图像上的褪色效果。

有没有办法提高质量? 还是专门的图书馆?

让我们从明显的……开始吧

  • A4纸是21.0cm×29.7cm。
  • 在300dpi,这使它成为2480.315×3507.874像素
  • 在72dpi,这使它成为595.2756×841.8898像素

为什么这很重要? Java以72dpi渲染到屏幕,但能够以300dpi(上下都打印,但这是个不错的数字)。 这意味着,粗略地说,您需要将屏幕图像缩放4倍。 如此处所示,向上缩放永远不会令人愉快

更好的解决方案是使用打印机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; } 

在给定DPI下从cm转换为像素。

现在到了有趣的部分。 您“可以”使用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打包并打印到打印机更容易,您也可以“绘制”到组件(或图像)

例如…

 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

ScaleComparison

 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