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
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...
72dpi test...
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;
}
}