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
72dpi test...
- Original size = 1667x1609
- cmSize = 14.11396110802889x13.622893474996092
- Target (pixel) size = 400x386
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