繁体   English   中英

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

[英]Storing DPI and Paper Size information in a JPEG with Java

我有以下代码:

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和纸张尺寸信息以帮助打印操作?

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

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

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

设置图像元数据的规范是标准(插件中性)元数据格式规范。 奇怪的是,规范是XML模式。 这是正确的,如果你想设置DPI,你必须通过使用IIOMetadataNodes构建DOM -tree 并将其与其余部分合并来实现 叹..

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

无论如何,当您想要设置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。 如果你给这些类他们想要的东西,位图就会很好。 要确保您的导出代码也适用于正确实现规范的平台,它必须检查正在使用的实际实现类并补偿错误。

如果您发现自己处于类似情况,请确保您的解决方法代码能够在最终修复错误时应对。 更多关于这一点的文章' J2SE api中的错误如何可以咬你两次 '。

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

这个问题已经部分回答: 用Java编写dpi元数据到jpeg图像

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

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

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

我发现此帖子用于在PNG文件上设置DPI 它指出您应该使用'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属性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM