Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
108 views
in Technique[技术] by (71.8m points)

java - Printable prints BufferedImage with incorrect size

So yeah what I am trying here is printing a BufferedImage, all works just fine until you see the outcome. The outcome is to big, the print is to large and doesn't it scales up everything when printing for some reason. I used ((MM * DPI)/25,4) to calculate the correct pixel length according to paper size from Millimeters but when I print it its to big.

This is the code I wrote for it:

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

So if someone could help me scale the image before printing it according to the printers scale factor, because I am assuming the printers DPI is causing this, so that the image is the exact size in Millimeters when printed exact to the size input in pixels.
That would be great.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I'm not sure if this is an answer per se, but it solves one of the "niggly" issues I'm having.

I think the problem I have is you don't have a source DPI, so it's not possible to convert from one context to another. Let's say you have a image of 200x200, what does that actually mean?

Without the DPI it's meaningless. If the image is 300dpi, then we could use pixels / dpi = inches = 200 / 72 = 0.667 inches. Then we can convert that to pixels @ 72dpi using inches * dpi = 0.667 * 72 = 48

Now, the question the becomes, how do I get the DPI of an image. That's not nearly as easy as it sounds...

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) {
            }
        }
    }
}

Update with preview example

This example basically uses the images own DPI and a target DPI to produce a "print preview"

My test image is 1667x1609 @ 300dpi

300dpi test...

  • Original size = 1667x1609
  • cmSize = 14.11396110802889x13.622893474996092
  • Target (pixel) size = 1667x1609

enter image description here

72dpi test...

  • Original size = 1667x1609
  • cmSize = 14.11396110802889x13.622893474996092
  • Target (pixel) size = 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_INC

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...