TwelveMonkeys ImageIO
| Language | Java 8 or later |
|---|---|
| License | BSD-3-Clause |
| Repository | github.com/haraldk/TwelveMonkeys |
| Documentation | JPEG plugin wiki, README |
| Depends on | TwelveMonkeys common and imageio-core/metadata modules (pulled in by Maven) |
| Read / write | yes / yes; JFIF, Exif, CMYK, YCCK, lossless JPEG (read), JFIF/JFXX/Exif thumbnails |
TwelveMonkeys is a collection of plugins for javax.imageio. Its JPEG plugin registers itself ahead of the JDK’s reader, so existing ImageIO.read(file) calls start working on files the JDK rejects:
- CMYK and Adobe YCCK JPEGs (from print workflows and Photoshop), converted to RGB using the embedded ICC profile;
- YCbCr files without a JFIF marker, odd
APP14segments, 16-bit quantization tables in 8-bit files; - corrupt ICC profiles and truncated or slightly broken files, which produce warnings instead of exceptions;
- lossless JPEG (
SOF3), used in medical and scientific images.
It also reads JFIF, JFXX and Exif thumbnails and can write CMYK JPEGs. For encoding ordinary RGB images it delegates to the JDK writer. It doesn’t apply the Exif orientation; read the tag with metadata-extractor. The project also has plugins for TIFF, PSD, WebP (read), BMP, ICO, PICT, SVG and others.
Installation#
Maven:
<dependency>
<groupId>com.twelvemonkeys.imageio</groupId>
<artifactId>imageio-jpeg</artifactId>
<version>3.15.3</version>
</dependency>
Gradle:
implementation("com.twelvemonkeys.imageio:imageio-jpeg:3.15.3")
Basic usage: read any JPEG with ImageIO#
No code changes: with the JAR on the classpath, the standard API uses the plugin.
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class ReadCmyk {
public static void main(String[] args) throws IOException {
// works for CMYK / YCCK files that throw "Unsupported Image Type" with the plain JDK
BufferedImage image = ImageIO.read(new File("cmyk.jpg"));
System.out.println(image.getWidth() + " x " + image.getHeight() + ", type " + image.getType());
ImageIO.write(image, "jpg", new File("rgb.jpg"));
}
}
How to check that the TwelveMonkeys JPEG reader is used#
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import java.util.Iterator;
Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName("JPEG");
while (readers.hasNext()) {
System.out.println(readers.next().getClass().getName());
}
// first line: com.twelvemonkeys.imageio.plugins.jpeg.JPEGImageReader
How to read the embedded thumbnail of a JPEG#
Exif and JFIF thumbnails load without decoding the full image, which is fast for photo browsers:
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.image.BufferedImage;
import java.io.File;
try (ImageInputStream in = ImageIO.createImageInputStream(new File("photo.jpg"))) {
ImageReader reader = ImageIO.getImageReaders(in).next();
try {
reader.setInput(in);
if (reader.getNumThumbnails(0) > 0) {
BufferedImage thumb = reader.readThumbnail(0, 0);
ImageIO.write(thumb, "jpg", new File("thumb.jpg"));
}
} finally {
reader.dispose();
}
}
How to use TwelveMonkeys in a web application#
ImageIO’s plugin registry is global to the JVM and doesn’t see JARs in WEB-INF/lib reliably. Add the servlet module and its context listener, which loads plugins on startup and unloads them on redeploy:
<dependency>
<groupId>com.twelvemonkeys.servlet</groupId>
<artifactId>servlet</artifactId>
<version>3.15.3</version>
<classifier>jakarta</classifier> <!-- omit for javax.servlet -->
</dependency>
<listener>
<listener-class>com.twelvemonkeys.servlet.image.IIOProviderContextListener</listener-class>
</listener>
In Spring Boot and other fat-JAR applications the plugins are found without the listener.
Tips#
- With shaded or fat JARs, merge the
META-INF/services/javax.imageio.spi.*files (Maven ShadeServicesResourceTransformer), or ImageIO won’t find the plugins. - Call
ImageIO.scanForPlugins()if you add the JAR to a class loader at runtime.