java - Printable 打印尺寸不正确的 BufferedImage

标签 java swing printing resize bufferedimage

所以,是的,我在这里尝试的是打印 BufferedImage,一切正常,直到您看到结果。结果太大了,打印太大了,并且在打印时由于某种原因它不会放大所有内容。 我使用 ((MM * DPI)/25,4) 根据纸张尺寸从毫米计算正确的像素长度,但当我打印它时它很大。

这是我为其编写的代码:

package frik.main;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;

import java.awt.event.*;

import javax.swing.*;
import frik.data.Config;
import frik.utils.ImgUtil;

public class Previewer implements Config, Printable, ActionListener{

    private JFrame Frame;
    private JPanel ImagePanel;
    private JLabel PicLabel;
    private JButton PrintButton;
    private static BufferedImage before;
    private static boolean Scaled;

    public Previewer(BufferedImage Image, boolean scaled){
        this.before = Image;
        this.Scaled = scaled;
        loop();

    }

    public int print(Graphics g, PageFormat pf, int page) throws PrinterException{
        if (page > 0) { 
            return Printable.NO_SUCH_PAGE;
        }
        Graphics2D g2d = (Graphics2D)g;
        g2d.translate(pf.getImageableX(), pf.getImageableY());

        g.drawImage(0, 0, null);

        return Printable.PAGE_EXISTS;
    }

    public void actionPerformed(ActionEvent e){
        PrinterJob job = PrinterJob.getPrinterJob();
        job.setPrintable(this);
        boolean ok = job.printDialog();
        if (ok) {
            try {
                job.print();
            } catch (PrinterException ex) {
                JOptionPane.showMessageDialog(null, "The Printjob did not successfully complete.", "Print Failure.", JOptionPane.WARNING_MESSAGE);
            }
        }
    }

    public void loop(){
        UIManager.put("swing.boldMetal", Boolean.FALSE);
        Frame = new JFrame("Mold Preview");
        ImagePanel = new JPanel();
        PrintButton = new JButton("Print Mold");

        Frame.addWindowListener(new WindowAdapter() {
               public void windowClosing(WindowEvent e) {System.exit(0);}
            });

        if(Scaled){
            PicLabel = new JLabel(new ImageIcon(ImgUtil.scaleImage(PAPER_WIDTH / 3, PAPER_HEIGHT / 3, before)));
        }else if (!Scaled){
            PicLabel = new JLabel(new ImageIcon(before));
        }

        ImagePanel.setBackground(Color.orange);
        ImagePanel.add(PicLabel);
        Frame.add("Center", ImagePanel);

        PrintButton.addActionListener(this);
        Frame.add("North", PrintButton);

        Frame.pack();
        Frame.setVisible(true);
        Frame.setResizable(false);

    }

    public static void main(String args[]){
        new Previewer(before, Scaled);
        //////////////////////////////
    }
}

因此,如果有人可以在根据打印机比例因子打印图像之前帮助我缩放图像,因为我假设打印机 DPI 导致了这种情况,因此当精确打印到输入尺寸时,图像的尺寸以毫米为单位以像素为单位。
那太好了。

最佳答案

我不确定这本身是否是一个答案,但它解决了我遇到的“棘手”问题之一。

认为我遇到的问题是您没有源 DPI,因此无法从一种上下文转换到另一种上下文。假设您有一张 200x200 的图像,这实际上意味着什么?

没有 DPI 就毫无意义。如果图像的分辨率为 300dpi,那么我们可以使用像素/dpi = 英寸 = 200/72 = 0.667 英寸。然后我们可以使用 inchs * dpi = 0.667 * 72 = 48

将其转换为像素 @ 72dpi

现在的问题是,如何获取图像的 DPI。这并不像听起来那么容易......

import core.ui.UIUtilities;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestDPI {

    public static final float INCH_PER_MM = 25.4f;

    public static void main(String[] args) {
        File imageFile = new File("/path/to/your/image");
        ImageInputStream iis = null;
        try {
            iis = ImageIO.createImageInputStream(imageFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
            if (!readers.hasNext()) {
                throw new IOException("Bad format, no readers");
            }
            ImageReader reader = readers.next();
            reader.setInput(iis);
            IIOMetadata meta = reader.getImageMetadata(0);

            Node root = meta.getAsTree("javax_imageio_1.0");
            NodeList nl = root.getChildNodes();
            float horizontalPixelSize = 0;
            float verticalPixelSize = 0;
            for (int index = 0; index < nl.getLength(); index++) {
                Node child = nl.item(index);
                if ("Dimension".equals(child.getNodeName())) {
                    NodeList dnl = child.getChildNodes();
                    for (int inner = 0; inner < dnl.getLength(); inner++) {
                        child = dnl.item(inner);
                        if ("HorizontalPixelSize".equals(child.getNodeName())) {
                            horizontalPixelSize = Float.parseFloat(child.getAttributes().getNamedItem("value").getNodeValue());
                        } else if ("VerticalPixelSize".equals(child.getNodeName())) {
                            verticalPixelSize = Float.parseFloat(child.getAttributes().getNamedItem("value").getNodeValue());
                        }
                    }
                }
            }
            // As "I" understand it.  The horizontalPixelSize and verticalPixelSize
            // are the number of millimeters per pixel that should be occupied...
            System.out.println((INCH_PER_MM / horizontalPixelSize) + "x" + (INCH_PER_MM / verticalPixelSize));

        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                iis.close();
            } catch (Exception e) {
            }
        }
    }
}

更新预览示例

此示例基本上使用图像自己的 DPI 和目标 DPI 来生成“打印预览”

我的测试图像是 1667x1609 @ 300dpi

300dpi 测试...

  • 原始尺寸 = 1667x1609
  • 厘米尺寸 = 14.11396110802889x13.622893474996092
  • 目标(像素)大小 = 1667x1609

enter image description here

72dpi 测试...

  • 原始尺寸 = 1667x1609
  • 厘米尺寸 = 14.11396110802889x13.622893474996092
  • 目标(像素)大小 = 400x386

enter image description here

import static core.ui.ImageUtilities.getScaleFactor;
import static core.ui.ImageUtilities.getScaleFactorToFit;
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.RenderingHints;
import java.awt.Transparency;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.stream.ImageInputStream;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class TestPrintPreview {

    public static void main(String[] args) {
        new TestPrintPreview();
    }

    public TestPrintPreview() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                File imageFile = new File("C:\\hold\\thumbnails\\RentAZilla-300dpi.png");

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new JScrollPane(new PreviewPane(imageFile, 300)));
                frame.setSize(400, 400);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    // The size of an A4 sheet in CMs
    public static final double[] A4_PAPER_SIZE = new double[]{21.0, 29.7};
    // The number of CMs per Inch
    public static final double CM_PER_INCH = 0.393700787d;
    // The number of Inches per CMs
    public static final double INCH_PER_CM = 2.545d;
    // The numer of Inches per mm's
    public static final double INCH_PER_MM = 25.45d;

    public class PreviewPane extends JPanel {

        private BufferedImage img;
        private float targetDPI;

        private BufferedImage gridBackground;

        public PreviewPane(File imageFile, float outputDPI) {
            // This determines the output DPI we want...
            targetDPI = outputDPI;
            try {
                // Get the DPI from the image...
                double[] imgDPI = getDPI(imageFile);
                // Read the image
                img = ImageIO.read(imageFile);

                // Output the original size...
                System.out.println("Original size = " + img.getWidth() + "x" + img.getHeight());

                // Calculate the size of the image in cm's
                double cmWidth = pixelsToCms(img.getWidth(), imgDPI[0]);
                double cmHeight = pixelsToCms(img.getHeight(), imgDPI[1]);

                System.out.println("cmSize = " + cmWidth + "x" + cmHeight);

                // Calculate the new image size based on the target DPI and
                // the cm size of the image...
                int imgWidth = (int) Math.round(cmsToPixel(cmWidth, targetDPI));
                int imgHeight = (int) Math.round(cmsToPixel(cmHeight, targetDPI));
                System.out.println("Target size = " + imgWidth + "x" + imgHeight);

                // Create a scaled instance of the image to fit within the 
                // target boundries
                img = getScaledInstanceToFit(img, new Dimension(imgWidth, imgHeight));

            } catch (IOException ex) {
                Logger.getLogger(TestPrintPreview.class.getName()).log(Level.SEVERE, null, ex);
            }
            setBackground(Color.WHITE);
        }

        @Override
        public Dimension getPreferredSize() {
            // Return the size of the component based on the size of 
            // an A4 sheet of paper and the target DPI
            return new Dimension(
                    (int) Math.round(cmsToPixel(A4_PAPER_SIZE[0], targetDPI)),
                    (int) Math.round(cmsToPixel(A4_PAPER_SIZE[1], targetDPI)));
        }

        /**
         * Generates a grid of 1x1 cm cells.  This is used to allow you
         * to compare the differences of different DPI and ensure that the
         * output is what you are expecting...
         * @return 
         */
        protected BufferedImage getGridBackground() {
            if (gridBackground == null) {
                // Calculate the width and height we need...
                int width = (int) Math.round(cmsToPixel(A4_PAPER_SIZE[0], targetDPI));
                int height = (int) Math.round(cmsToPixel(A4_PAPER_SIZE[1], targetDPI));

                // Create the grid...
                gridBackground = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = gridBackground.createGraphics();
                // Calculate the size of each cell (1cm square)
                double cmAsPixel = cmsToPixel(1, targetDPI);
                float xPos = 0;
                float yPos = 0;
                g2d.setColor(new Color(225, 0, 0, 128));
                int count = 0;
                Font font = g2d.getFont();
                g2d.setFont(font.deriveFont(8f));
                FontMetrics fm = g2d.getFontMetrics();
                // Draw the horizontal lines
                while (xPos < gridBackground.getWidth()) {
                    g2d.draw(new Line2D.Float(xPos, 0, xPos, gridBackground.getHeight()));
                    // Add the text markers...
                    String text = (count++) + "cm";
                    float x = xPos - fm.stringWidth(text);
                    g2d.drawString(text, x, fm.getAscent());
                    xPos += cmAsPixel;
                }
                // Draw the vertical lines
                count = 0;
                while (yPos < gridBackground.getHeight()) {
                    g2d.draw(new Line2D.Float(0, yPos, gridBackground.getWidth(), yPos));
                    // Add the text markers
                    String text = (count++) + "cm";
                    float y = (yPos - fm.getHeight()) + fm.getAscent();
                    g2d.drawString(text, 0, y);
                    yPos += cmAsPixel;
                }
                g2d.dispose();
            }
            return gridBackground;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            // Paint the image...
            g2d.drawImage(img, 0, 0, this);
            // Paint the grid...
            g2d.drawImage(getGridBackground(), 0, 0, this);
            g2d.dispose();
        }
    }

    /**
     * Converts the given pixels to cm's based on the supplied DPI
     * @param pixels
     * @param dpi
     * @return 
     */
    public static double pixelsToCms(double pixels, double dpi) {
        return inchesToCms(pixels / dpi);
    }

    /**
     * Converts the given cm's to pixels based on the supplied DPI
     * @param cms
     * @param dpi
     * @return 
     */
    public static double cmsToPixel(double cms, double dpi) {
        return cmToInches(cms) * dpi;
    }

    /**
     * Converts the given cm's to inches
     * @param cms
     * @return 
     */
    public static double cmToInches(double cms) {
        return cms * CM_PER_INCH;
    }

    /**
     * Converts the given inches to cm's 
     * @param inch
     * @return 
     */
    public static double inchesToCms(double inch) {
        return inch * INCH_PER_CM;
    }

    /**
     * Gets the DPI for the specified image.  This does return the horizontal
     * and vertical DPI, but you could conceivably use just use one of the values
     * @param imageFile
     * @return
     * @throws IOException 
     */
    public double[] getDPI(File imageFile) throws IOException {

        double[] dpi = new double[]{72, 72};

        ImageInputStream iis = null;
        try {
            iis = ImageIO.createImageInputStream(imageFile);
            Iterator<ImageReader> readers = ImageIO.getImageReaders(iis);
            if (!readers.hasNext()) {
                throw new IOException("Bad format, no readers");
            }
            ImageReader reader = readers.next();
            reader.setInput(iis);
            IIOMetadata meta = reader.getImageMetadata(0);

            Node root = meta.getAsTree("javax_imageio_1.0");
            NodeList nl = root.getChildNodes();
            float horizontalPixelSize = 0;
            float verticalPixelSize = 0;
            for (int index = 0; index < nl.getLength(); index++) {
                Node child = nl.item(index);
                if ("Dimension".equals(child.getNodeName())) {
                    NodeList dnl = child.getChildNodes();
                    for (int inner = 0; inner < dnl.getLength(); inner++) {
                        child = dnl.item(inner);
                        if ("HorizontalPixelSize".equals(child.getNodeName())) {
                            horizontalPixelSize = Float.parseFloat(child.getAttributes().getNamedItem("value").getNodeValue());
                        } else if ("VerticalPixelSize".equals(child.getNodeName())) {
                            verticalPixelSize = Float.parseFloat(child.getAttributes().getNamedItem("value").getNodeValue());
                        }
                    }
                }
            }

            dpi = new double[]{(INCH_PER_MM / horizontalPixelSize), (INCH_PER_MM / verticalPixelSize)};
        } finally {
            try {
                iis.close();
            } catch (Exception e) {
            }
        }

        return dpi;
    }

    /**
     * Returns a scaled instance of the image to fit within the specified 
     * area.  This means that the image is guaranteed to be <= size.width and
     * <= size.height
     * @param img
     * @param size
     * @return 
     */
    public static BufferedImage getScaledInstanceToFit(BufferedImage img, Dimension size) {
        double scaleFactor = getScaleFactorToFit(img, size);
        return getScaledInstance(img, scaleFactor);
    }

    public static double getScaleFactorToFit(BufferedImage img, Dimension size) {

        double dScale = 1;
        if (img != null) {
            int imageWidth = img.getWidth();
            int imageHeight = img.getHeight();

            dScale = getScaleFactorToFit(new Dimension(imageWidth, imageHeight), size);
        }

        return dScale;

    }

    /**
     * Returns the required scale factor to fit the original size into the toFit
     * size.
     * @param original
     * @param toFit
     * @return 
     */
    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;

    }

    /**
     * Returns the scale factor required to go from the master size to the 
     * target size
     * @param iMasterSize
     * @param iTargetSize
     * @return 
     */
    public static double getScaleFactor(int iMasterSize, int iTargetSize) {
        return (double) iTargetSize / (double) iMasterSize;
    }

    /**
     * Returns a scaled instance of the image based on the supplied scale factor.
     * 
     * The images width and height are multiplied by the supplied scale factor
     * @param img
     * @param dScaleFactor
     * @return 
     */
    protected static BufferedImage getScaledInstance(BufferedImage img, double dScaleFactor) {
        BufferedImage imgScale = img;

        int iImageWidth = (int) Math.round(img.getWidth() * dScaleFactor);
        int iImageHeight = (int) Math.round(img.getHeight() * dScaleFactor);

        if (dScaleFactor <= 1.0d) {
            imgScale = getScaledDownInstance(img, iImageWidth, iImageHeight);
        } else {
            imgScale = getScaledUpInstance(img, iImageWidth, iImageHeight);
        }
        return imgScale;
    }

    /**
     * Scales the specified image down to be less then equal to the target width
     * and height.
     * 
     * The image is scaled using a divide an conquer approach to provide
     * the best scaling possible
     * @param img
     * @param targetWidth
     * @param targetHeight
     * @return 
     */
    protected static BufferedImage getScaledDownInstance(BufferedImage img,
            int targetWidth,
            int targetHeight) {

        int type = (img.getTransparency() == Transparency.OPAQUE)
                ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = (BufferedImage) img;

        if (targetHeight > 0 || targetWidth > 0) {
            int w, h;
            w = img.getWidth();
            h = img.getHeight();

            do {
                if (w > targetWidth) {
                    w /= 2;
                    if (w < targetWidth) {
                        w = targetWidth;
                    }
                }

                if (h > targetHeight) {
                    h /= 2;
                    if (h < targetHeight) {
                        h = targetHeight;
                    }
                }

                BufferedImage tmp = new BufferedImage(Math.max(w, 1), Math.max(h, 1), type);
                Graphics2D g2 = tmp.createGraphics();
                g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                g2.drawImage(ret, 0, 0, w, h, null);
                g2.dispose();

                ret = tmp;
            } while (w != targetWidth || h != targetHeight);
        } else {
            ret = new BufferedImage(1, 1, type);
        }

        return ret;

    }

    /**
    /**
     * Scales the specified image up
     * 
     * The image is scaled using a divide an conquer approach to provide
     * the best scaling possible
     * @param img
     * @param targetWidth
     * @param targetHeight
     * @return 
     */
    protected static BufferedImage getScaledUpInstance(BufferedImage img,
            int targetWidth,
            int targetHeight) {

        int type = BufferedImage.TYPE_INT_ARGB;

        BufferedImage ret = (BufferedImage) img;
        int w, h;
        w = img.getWidth();
        h = img.getHeight();

        do {
            if (w < targetWidth) {
                w *= 2;
                if (w > targetWidth) {
                    w = targetWidth;
                }
            }

            if (h < targetHeight) {
                h *= 2;
                if (h > targetHeight) {
                    h = targetHeight;
                }
            }

            BufferedImage tmp = new BufferedImage(w, h, type);
            Graphics2D g2 = tmp.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
            g2.drawImage(ret, 0, 0, w, h, null);
            g2.dispose();

            ret = tmp;
            tmp = null;
        } while (w != targetWidth || h != targetHeight);
        return ret;
    }
}

关于java - Printable 打印尺寸不正确的 BufferedImage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18460008/

相关文章:

java - 在 testng XML 中添加系统属性?

java - 如何使用后退按钮处理对话框

java - 如何删除 JScrollPane 内 JTable 顶部的 A?

printing - 如何以编程方式访问打印机的墨水量?

java - 仅打印一次特定的打印语句

java - 编写这些java数组的最佳方法是什么?

Java Swing Worker 没有停止

java - 在 Swing 中使用单例设计模式

css - 使用EPSON的 "FontA11"打印HTML页面

java - 简单框架 XML 删除空属性