我有以下代码:
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和纸张尺寸信息以帮助打印操作?
答案 0 :(得分:12)
幸运的是,java image I/O API让你这样做。它还允许以与格式无关的方式设置图像元数据。例如,指定所需的DPI,这就是我想要做的。
事实证明,设定dpi并不像预期那么简单......
java image I/O API为plug-in based,对于您希望使用的每种文件格式,必须提供相应的插件。 API为编写插件中性代码提供了必要的abstractions,并且具有可在运行时查询的可用实现的存储库。从J2SE 6开始,每个JRE都必须提供PNG,JPG和BMP文件格式的插件(以及其他一些我尚未尝试过的插件)。
设置图像元数据的规范是standard (plug-in neutral)元数据格式规范。奇怪的是,规范是XML模式。这是正确的,如果你想设置DPI,你必须通过使用DOM和IIOMetadataNodes构建merge it - 树来完成它!叹息..
请注意插件对标准元数据的支持可能不同:
无论如何,当您想要设置DPI时,相关标签是HorizontalPixelSize和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属性。