I have the following code:
ImageIO.write(originalImage, OUTPUT_TYPE, resultOutput);
This is an invocation of the following javax.imag
This question is already partially answered: Write dpi metadata to a jpeg image in Java
From the image editor GIMP I know that .png has a resolution (DPI) one may save as meta data. There is EXIF data for JPEG, info of the camera in general, including resolution.
You may consider using Commons Sanselan, instead of ImageIO for this task.
See http://commons.apache.org/sanselan/whysanselan.html for more info.
Luckily, the java image I/O API lets you do just that. It also allows for setting the image metadata in a format-independent way. For example to specify the desired DPI, which was what I wanted to do.
As it turned out, setting the dpi was less straightforward than expected…
The java image I/O API is plug-in based, for every file format you wish to use an appropriate plug-in must be available. The API provides the necessary abstractions for writing plug-in neutral code and has a repository of available implementations that you can query at run-time. As of J2SE 6, every JRE has to provide plug-ins for PNG, JPG and BMP file formats (plus some others that I haven’t tried out yet).
The spec for setting image metadata is the standard (plug-in neutral) metadata format specification. Strangely enough, the spec is an XML schema. That’s right, if you want to set the DPI, you’ll have to do it by building a DOM-tree using IIOMetadataNodes and merge it in with the rest! Sigh..
Beware that plug-ins can differ in their support for the standard metadata :
Anyway, the relevant tags when you want to set the DPI are HorizontalPixelSize and VerticalPixelSize :
<!ELEMENT "HorizontalPixelSize" EMPTY>
<!-- The width of a pixel, in millimeters,
as it should be rendered on media -->
<!ATTLIST "HorizontalPixelSize" "value" #CDATA #REQUIRED>
<!-- Data type: Float -->
<!ELEMENT "VerticalPixelSize" EMPTY>
<!-- The height of a pixel, in millimeters,
as it should be rendered on media -->
<!ATTLIST "VerticalPixelSize" "value" #CDATA #REQUIRED>
<!-- Data type: Float -->
Note that the spec clearly states that both have to be expressed in millimeters per dot. How to disregard your own spec, Sun style
Sun has implemented this metadata spec for their PNG and JPG plug-ins and includes it in their current JDK and JRE distributions. The relevant classes are
com.sun.imageio.plugins.png.PNGImageWriter
com.sun.imageio.plugins.jpeg.JPEGImageWriter
You can tell from the com.sun package that they’re not part of the J2SE API, but specific to Sun’s implementation.
Remember how the spec required millimeters per dot? Well, have a look at the following table to see how Sun actually implemented the spec :
plug-in unit bug report date reported
PNGImageWriter dots per millimeter bug 5106305 23 sep 2004
JPEGImageWriter decimeter per dot bug 6359243 05 dec 2005
These bugs have been known for a very long time and their fixes are really simple. Unfortunately, they were given a low priority and haven’t even been evaluated at the time of writing (July 2008). Great. Now what?
Well, because the workaround is so trivial I decided to stick with the image I/O API. If you give these classes what they want, the bitmaps will come out fine. To ensure that your export code also works on platforms that implement the spec correctly, it must check the actual implementation classes that are being used and compensate for the bugs.
If you find yourself in a similar situation, make sure your workaround code will be able to cope when the bugs are fixed eventually. More on this in the article 'How bugs in the J2SE api can bite you twice'.
Oh, and if you use instanceof to check for instances of a buggy class that is not guaranteed to exist on all platforms, be sure to catch NoClassDefFoundError ;)
I found this post for setting DPI on PNG Files. It pointed out that you should use 'metadata.mergeTree' to properly save your metadata.
With that in mind, here is some working groovy code that takes a BMP file and creates a JPG file at arbitrary DPI:
import java.awt.image.BufferedImage
import java.io.File
import java.util.Hashtable
import java.util.Map
import javax.imageio.*
import javax.imageio.stream.*
import javax.imageio.metadata.*
import javax.imageio.plugins.jpeg.*
import org.w3c.dom.*
File sourceFile = new File("sample.bmp")
File destinationFile = new File("sample.jpg")
dpi = 100
BufferedImage sourceImage = ImageIO.read(sourceFile)
ImageWriter imageWriter = ImageIO.getImageWritersBySuffix("jpeg").next();
ImageOutputStream ios = ImageIO.createImageOutputStream(destinationFile);
imageWriter.setOutput(ios);
def jpegParams = imageWriter.getDefaultWriteParam();
IIOMetadata data = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(sourceImage), jpegParams);
Element tree = (Element)data.getAsTree("javax_imageio_jpeg_image_1.0");
Element jfif = (Element)tree.getElementsByTagName("app0JFIF").item(0);
jfif.setAttribute("Xdensity", Integer.toString(dpi));
jfif.setAttribute("Ydensity", Integer.toString(dpi));
jfif.setAttribute("resUnits", "1"); // density is dots per inch
data.mergeTree("javax_imageio_jpeg_image_1.0",tree)
// Write and clean up
imageWriter.write(data, new IIOImage(sourceImage, null, data), jpegParams);
ios.close();
imageWriter.dispose();
Worked fine for me in that OSX's Preview app and Gimp both reported that the resulting image was 100 DPI. As to Paper Size...I imagine this is directly determined by DPI? I couldn't find any JPEG property that would set that particular value.