将JPanel保存为图像高清质量

时间:2015-02-10 08:58:54

标签: java image graphics2d

我正在尝试将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..

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

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

1 个答案:

答案 0 :(得分:3)

让我们从明显的......

开始
  • A4纸是21.0cm×29.7cm。
  • 在300dpi时,使其成为2480.315x3507.874像素
  • 在72dpi,这使它成为595.2756x841.8898像素

为什么这么重要? 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();
    }
}

在屏幕上看起来像......

Screen

在纸面上它看起来像(缩小为SO)

Printed

现在说过,我强烈建议您学习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