如何在Java中保存带有tEXt或iTXt块的PNG?

13
我目前正在使用javax.imageio.ImageIO编写PNG文件。 我想包含tEXt块(以及在此处列出的任何块),但看不到这样做的方法。
根据com.sun.imageio.plugins.png.PNGMetadata的外观,应该是可能的。
如果有任何提示或答案,我将非常感激。
M.
6个回答

10

在一些反编译之后,我找到了如下解决方案...

RenderedImage image = getMyImage();         
Iterator<ImageWriter> iterator = ImageIO.getImageWritersBySuffix( "png" );

if(!iterator.hasNext()) throw new Error( "No image writer for PNG" );

ImageWriter imagewriter = iterator.next();
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
imagewriter.setOutput( ImageIO.createImageOutputStream( bytes ) ); 

// Create & populate metadata
PNGMetadata metadata = new PNGMetadata();
// see http://www.w3.org/TR/PNG-Chunks.html#C.tEXt for standardized keywords
metadata.tEXt_keyword.add( "Title" );
metadata.tEXt_text.add( "Mandelbrot" );
metadata.tEXt_keyword.add( "Comment" );
metadata.tEXt_text.add( "..." );
metadata.tEXt_keyword.add( "MandelbrotCoords" ); // custom keyword
metadata.tEXt_text.add( fractal.getCoords().toString() );           

// Render the PNG to memory
IIOImage iioImage = new IIOImage( image, null, null );
iioImage.setMetadata( metadata ); // Attach the metadata
imagewriter.write( null, iioImage, null );

1

使用Java 1.6,我修改了Mike的代码以

Node document = domresult.getNode();

用他的代码行之外

Document document = dom.getResult();

此外,我建议添加一行代码。
writer.dispose()

在工作完成后,以便释放作者持有的任何资源。


1

我知道这个问题早就有了答案,但如果你想不借助于"com.sun"层次结构来完成它,这里有一个快速而非常丑陋的例子,因为我在其他地方找不到这个文件的文档。

BufferedImage img = new BufferedImage(300, 300, BufferedImage.TYPE_INT_ARGB);

// Create a DOM Document describing the metadata;
// I've gone the quick and dirty route. The description for PNG is at
// [http://download.oracle.com/javase/1.4.2/docs/api/javax/imageio/metadata/doc-files/png_metadata.html][1]

Calendar c = Calendar.getInstance();
String xml = "<?xml version='1.0'?><javax_imageio_png_1.0><tIME year='"+c.get(c.YEAR)+"' month='"+(c.get(c.MONTH)+1)+"' day='"+c.get(c.DAY_OF_MONTH)+"' hour='"+c.get(c.HOUR_OF_DAY)+"' minute='"+c.get(c.MINUTE)+"' second='"+c.get(c.SECOND)+"'/><pHYs pixelsPerUnitXAxis='"+11811+"' pixelsPerUnitYAxis='"+11811+"' unitSpecifier='meter'/></javax_imageio_png_1.0>";
DOMResult domresult = new DOMResult();
TransformerFactory.newInstance().newTransformer().transform(new StreamSource(new StringReader(xml)), domresult);
Document document = dom.getResult();

// Apply the metadata to the image
ImageWriter writer = (ImageWriter)ImageIO.getImageWritersBySuffix("png").next();
IIOMetadata meta = writer.getDefaultImageMetadata(new ImageTypeSpecifier(img), null);
meta.setFromTree(meta.getMetadataFormatNames()[0], document.getFirstChild());
FileOutputStream out = new FileOutputStream("out.png");
writer.setOutput(ImageIO.createImageOutputStream(out));
writer.write(new IIOImage(img, null, meta));
out.close();

1
我们在JGraphX项目中实现了这个功能。下载源代码并查看com.mxgraph.util.png包,你会发现我们从Apache Batik源代码中复制了三个编码类。使用示例可以在com.mxgraph.examples.swing.editor.EditorActions的saveXmlPng方法中找到。稍微编辑一下代码就像这样:
mxPngEncodeParam param = mxPngEncodeParam
  .getDefaultEncodeParam(image);
param.setCompressedText(new String[] { "mxGraphModel", xml });

// Saves as a PNG file
FileOutputStream outputStream = new FileOutputStream(new File(
  filename));
try
{
 mxPngImageEncoder encoder = new mxPngImageEncoder(outputStream,
   param);

 if (image != null)
 {
  encoder.encode(image);
 }
}
finally
{
 outputStream.close();
}

在这里,image 是 BufferedImage 对象,将生成 .PNG 文件,xml 是我们希望放置在 iTxt 部分的字符串。"mxGraphModel" 是该 xml 字符串的键(该部分包含一些键值对),显然你需要用自己的键来替换它。

此外,在 com.mxgraph.util.png 包中,我们编写了一个非常简单的类,可以提取 iTxt 而不处理整个图像。你可以使用 mxPngEncodeParam.setText() 替代 setCompressedText() 来应用相同的思路于 tEXt 块,但压缩文本部分确实允许更大的文本内容。


0

虽然这是一个老问题,但是PNGJ可以完全控制读写PNG块。


0

那不仅仅是用于解码吗? - axel22

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接