com.drew.imaging.ImageMetadataReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of metadata-extractor Show documentation
Show all versions of metadata-extractor Show documentation
Java library for extracting EXIF, IPTC, XMP, ICC and other metadata from image and video files.
/*
* Copyright 2002-2012 Drew Noakes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* More information about this project is available at:
*
* http://drewnoakes.com/code/exif/
* http://code.google.com/p/metadata-extractor/
*/
package com.drew.imaging;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.psd.PsdMetadataReader;
import com.drew.imaging.tiff.TiffMetadataReader;
import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataException;
import com.drew.metadata.Tag;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.drew.metadata.exif.ExifThumbnailDirectory;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* Obtains metadata from all supported file formats, including JPEG, RAW (NEF/CRw/CR2) and TIFF.
* If the caller knows their file to be of a particular type, they may prefer to use the dedicated MetadataReader
* directly (JpegMetadataReader
for Jpeg files, or TiffMetadataReader
for TIFF and RAW files).
* The dedicated readers offer a very slight performance improvement, though for most scenarios it is simpler,
* more convenient and more robust to use this class.
*
* @author Drew Noakes http://drewnoakes.com
*/
public class ImageMetadataReader
{
private static final int JPEG_FILE_MAGIC_NUMBER = 0xFFD8;
private static final int MOTOROLA_TIFF_MAGIC_NUMBER = 0x4D4D; // "MM"
private static final int INTEL_TIFF_MAGIC_NUMBER = 0x4949; // "II"
private static final int PSD_MAGIC_NUMBER = 0x3842; // "8B" note that the full magic number is 8BPS
/**
* Reads metadata from an input stream. The file inputStream examined to determine its type and consequently the
* appropriate method to extract the data, though this inputStream transparent to the caller.
*
* @param inputStream a stream from which the image data may be read. The stream must be positioned at the
* beginning of the image data.
* @return a populated Metadata error containing directories of tags with values and any processing errors.
* @throws ImageProcessingException for general processing errors.
*/
@NotNull
public static Metadata readMetadata(@NotNull BufferedInputStream inputStream, boolean waitForBytes) throws ImageProcessingException, IOException
{
int magicNumber = readMagicNumber(inputStream);
return readMetadata(inputStream, null, magicNumber, waitForBytes);
}
/**
* Reads metadata from a file. The file is examined to determine its type and consequently the appropriate
* method to extract the data, though this is transparent to the caller.
*
* @param file a file from which the image data may be read.
* @return a populated Metadata error containing directories of tags with values and any processing errors.
* @throws ImageProcessingException for general processing errors.
*/
@NotNull
public static Metadata readMetadata(@NotNull File file) throws ImageProcessingException, IOException
{
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file));
int magicNumber;
try {
magicNumber = readMagicNumber(inputStream);
} finally {
inputStream.close();
}
return readMetadata(null, file, magicNumber, false);
}
@NotNull
private static Metadata readMetadata(@Nullable BufferedInputStream inputStream, @Nullable File file, int magicNumber, boolean waitForBytes) throws ImageProcessingException, IOException
{
assert(file!=null ^ inputStream!=null);
// This covers all JPEG files
if ((magicNumber & JPEG_FILE_MAGIC_NUMBER) == JPEG_FILE_MAGIC_NUMBER) {
if (inputStream != null)
return JpegMetadataReader.readMetadata(inputStream, waitForBytes);
else
return JpegMetadataReader.readMetadata(file);
}
// This covers all TIFF and camera RAW files
if (magicNumber == INTEL_TIFF_MAGIC_NUMBER || magicNumber == MOTOROLA_TIFF_MAGIC_NUMBER) {
if (inputStream != null)
return TiffMetadataReader.readMetadata(inputStream, waitForBytes);
else
return TiffMetadataReader.readMetadata(file);
}
// This covers PSD files
// TODO we should really check all 4 bytes of the PSD magic number
if (magicNumber == PSD_MAGIC_NUMBER) {
if (inputStream != null)
return PsdMetadataReader.readMetadata(inputStream, waitForBytes);
else
return PsdMetadataReader.readMetadata(file);
}
throw new ImageProcessingException("File format is not supported");
}
private static int readMagicNumber(@NotNull BufferedInputStream inputStream) throws IOException
{
inputStream.mark(2);
int magicNumber = inputStream.read() << 8 | inputStream.read();
inputStream.reset();
return magicNumber;
}
private ImageMetadataReader() throws Exception
{
throw new Exception("Not intended for instantiation");
}
/**
* An application entry point. Takes the name of one or more files as arguments and prints the contents of all
* metadata directories to System.out. If /thumb
is passed, then any thumbnail data will be
* written to a file with name of the input file having '.thumb.jpg' appended.
*
* @param args the command line arguments
*/
public static void main(@NotNull String[] args) throws MetadataException, IOException
{
Collection argList = new ArrayList(Arrays.asList(args));
boolean thumbRequested = argList.remove("/thumb");
boolean wikiFormat = argList.remove("/wiki");
if (argList.size() < 1) {
System.out.println("Usage: java -jar metadata-extractor-a.b.c.jar [] [/thumb] [/wiki]");
System.exit(1);
}
for (String filePath : argList) {
long startTime = System.nanoTime();
File file = new File(filePath);
if (!wikiFormat && argList.size()>1)
System.out.println("***** PROCESSING: " + filePath);
Metadata metadata = null;
try {
metadata = ImageMetadataReader.readMetadata(file);
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(1);
}
long took = System.nanoTime() - startTime;
if (!wikiFormat)
System.out.println("Processed " + (file.length()/(1024d*1024)) + "MB file in " + (took / 1000000d) + "ms");
if (wikiFormat) {
String fileName = file.getName();
String urlName = fileName.replace(" ", "%20"); // How to do this using framework?
ExifIFD0Directory exifIFD0Directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class);
String make = escapeForWiki(exifIFD0Directory.getString(ExifIFD0Directory.TAG_MAKE));
String model = escapeForWiki(exifIFD0Directory.getString(ExifIFD0Directory.TAG_MODEL));
System.out.println();
System.out.println("-----");
System.out.println();
System.out.printf("= %s - %s =%n", make, model);
System.out.println();
System.out.printf("%n", urlName);
System.out.printf("
%n", urlName);
System.out.println(fileName);
System.out.println("");
System.out.println();
System.out.println("|| *Directory* || *Tag Id* || *Tag Name* || *Tag Description* ||");
}
// iterate over the exif data and print to System.out
for (Directory directory : metadata.getDirectories()) {
for (Tag tag : directory.getTags()) {
String tagName = tag.getTagName();
String directoryName = directory.getName();
String description = tag.getDescription();
if (wikiFormat) {
System.out.printf("||%s||0x%s||%s||%s||%n",
escapeForWiki(directoryName),
Integer.toHexString(tag.getTagType()),
escapeForWiki(tagName),
escapeForWiki(description));
}
else
{
System.out.printf("[%s] %s = %s%n", directoryName, tagName, description);
}
}
// print out any errors
for (String error : directory.getErrors())
System.err.println("ERROR: " + error);
}
if (args.length > 1 && thumbRequested) {
ExifThumbnailDirectory directory = metadata.getDirectory(ExifThumbnailDirectory.class);
if (directory!=null && directory.hasThumbnailData()) {
System.out.println("Writing thumbnail...");
directory.writeThumbnail(args[0].trim() + ".thumb.jpg");
} else {
System.out.println("No thumbnail data exists in this image");
}
}
}
}
@Nullable
private static String escapeForWiki(@Nullable String text)
{
if (text==null)
return null;
text = text.replaceAll("(\\W|^)(([A-Z][a-z0-9]+){2,})", "$1!$2");
if (text!=null && text.length() > 120)
text = text.substring(0, 120) + "...";
if (text != null)
text = text.replace("[", "`[`").replace("]", "`]`").replace("<", "`<`").replace(">", "`>`");
return text;
}
}