使用Java在JPEG中存储DPI和纸张尺寸信息

时间:2011-12-23 17:10:33

标签: java jpeg dpi javax.imageio

我有以下代码:

ImageIO.write(originalImage, OUTPUT_TYPE, resultOutput);

这是对以下javax.imageio.ImageIO方法的调用:

public static boolean write(RenderedImage im,
                            String formatName,
                            File output)
                     throws IOException

这会将原始BMP图像转换为JGP输出。是否也可以在JPEG中存储DPI和纸张尺寸信息以帮助打印操作?

4 个答案:

答案 0 :(得分:12)

幸运的是,java image I/O API让你这样做。它还允许以与格式无关的方式设置图像元数据。例如,指定所需的DPI,这就是我想要做的。

事实证明,设定dpi并不像预期那么简单......

java image I/O APIplug-in based,对于您希望使用的每种文件格式,必须提供相应的插件。 API为编写插件中性代码提供了必要的abstractions,并且具有可在运行时查询的可用实现的存储库。从J2SE 6开始,每个JRE都必须提供PNG,JPG和BMP文件格式的插件(以及其他一些我尚未尝试过的插件)。

设置图像元数据的规范是standard (plug-in neutral)元数据格式规范。奇怪的是,规范是XML模式。这是正确的,如果你想设置DPI,你必须通过使用DOMIIOMetadataNodes构建merge it - 树来完成它!叹息..

请注意插件对标准元数据的支持可能不同:

无论如何,当您想要设置DPI时,相关标签是Horizo​​ntalPixelSize和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 -->

请注意,规范明确指出两者都必须以毫米每点表示。 如何忽视自己的规范,Sun风格

Sun为其PNG和JPG插件实现了此元数据规范,并将其包含在当前的JDK和JRE发行版中。相关的课程是

com.sun.imageio.plugins.png.PNGImageWriter
com.sun.imageio.plugins.jpeg.JPEGImageWriter

你可以从com.sun包中看出它们不是J2SE API的一部分,而是特定于Sun的实现。

还记得每个点的规格要求毫米吗?那么,看一下下表,了解Sun实际上如何实现规范:

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

这些错误已经有很长一段时间了,它们的修复非常简单。不幸的是,它们被赋予了低优先级,在撰写本文时(2008年7月)甚至没有被评估过。 大。现在怎么样?

好吧,因为解决方法非常简单,所以我决定坚持使用图像I / O API。如果你给这些类他们想要的东西,位图就会很好。为了确保您的导出代码也适用于正确实现规范的平台,它必须检查正在使用的实际实现类并补偿错误。

如果您发现自己处于类似情况,请确保您的解决方法代码能够在最终修复错误时应对。有关详情,请参阅文章“How bugs in the J2SE api can bite you twice”。

哦,如果你使用instanceof来检查所有平台上不能保证存在的错误类的实例,请务必捕获NoClassDefFoundError;)

答案 1 :(得分:4)

这个问题已经部分回答:Write dpi metadata to a jpeg image in Java

从图像编辑器GIMP我知道 .png 具有可以保存为元数据的分辨率(DPI)。 JPEG的 EXIF 数据,一般是相机的信息,包括分辨率。

答案 2 :(得分:3)

您可以考虑使用Commons Sanselan而不是ImageIO来完成此任务。

有关详细信息,请参阅http://commons.apache.org/sanselan/whysanselan.html

答案 3 :(得分:3)

我发现了post for setting DPI on PNG Files。它指出你应该使用'metadata.mergeTree'来正确保存你的元数据。

考虑到这一点,这里有一些工作的groovy代码,它接受一个BMP文件并在任意DPI创建一个JPG文件:

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();

在那个OSX的预览应用程序中,我工作得很好,Gimp都报告得到的图像是100 DPI。至于纸张尺寸......我想这是由DPI直接决定的?我找不到任何可以设置该特定值的JPEG属性。