Skip to content
Commits on Source (9)
......@@ -2,7 +2,6 @@
[![metadata-extractor build status](https://api.travis-ci.org/drewnoakes/metadata-extractor.svg)](https://travis-ci.org/drewnoakes/metadata-extractor)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.drewnoakes/metadata-extractor/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.drewnoakes/metadata-extractor)
[![Issue Stats](http://issuestats.com/github/drewnoakes/metadata-extractor/badge/pr?style=flat)](http://issuestats.com/github/drewnoakes/metadata-extractor)
[![Donate](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=TNXDJKCDV5Z2C&lc=GB&item_name=Drew%20Noakes&item_number=metadata%2dextractor&no_note=0&cn=Add%20a%20message%20%28optional%29%3a&no_shipping=1&currency_code=GBP&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
_metadata-extractor_ is a straightforward Java library for reading metadata from image files.
......@@ -11,11 +10,13 @@ _metadata-extractor_ is a straightforward Java library for reading metadata from
The easiest way is to install the library via its [Maven package](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.drewnoakes%22%20AND%20a%3A%22metadata-extractor%22).
```xml
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.10.0</version>
<version>2.11.0</version>
</dependency>
```
Alternatively, download it from the [releases page](https://github.com/drewnoakes/metadata-extractor/releases).
......@@ -32,30 +33,38 @@ With that `Metadata` instance, you can [iterate or query](https://github.com/dre
The library understands several formats of metadata, many of which may be present in a single image:
* [Exif](http://en.wikipedia.org/wiki/Exchangeable_image_file_format)
* [IPTC](http://en.wikipedia.org/wiki/IPTC)
* [XMP](http://en.wikipedia.org/wiki/Extensible_Metadata_Platform)
* [JFIF / JFXX](http://en.wikipedia.org/wiki/JPEG_File_Interchange_Format)
* [ICC Profiles](http://en.wikipedia.org/wiki/ICC_profile)
* [Photoshop](http://en.wikipedia.org/wiki/Photoshop) fields
* [WebP](http://en.wikipedia.org/wiki/WebP) properties
* [PNG](http://en.wikipedia.org/wiki/Portable_Network_Graphics) properties
* [BMP](http://en.wikipedia.org/wiki/BMP_file_format) properties
* [GIF](http://en.wikipedia.org/wiki/Graphics_Interchange_Format) properties
* [Exif](https://en.wikipedia.org/wiki/Exchangeable_image_file_format)
* [IPTC](https://en.wikipedia.org/wiki/IPTC)
* [XMP](https://en.wikipedia.org/wiki/Extensible_Metadata_Platform)
* [JFIF / JFXX](https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format)
* [ICC Profiles](https://en.wikipedia.org/wiki/ICC_profile)
* [Photoshop](https://en.wikipedia.org/wiki/Photoshop) fields
* [WebP](https://en.wikipedia.org/wiki/WebP) properties
* [WAV](https://en.wikipedia.org/wiki/WAV) properties
* [AVI](https://en.wikipedia.org/wiki/Audio_Video_Interleave) properties
* [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) properties
* [BMP](https://en.wikipedia.org/wiki/BMP_file_format) properties
* [GIF](https://en.wikipedia.org/wiki/Graphics_Interchange_Format) properties
* [ICO](https://en.wikipedia.org/wiki/ICO_(file_format)) properties
* [PCX](http://en.wikipedia.org/wiki/PCX) properties
* [PCX](https://en.wikipedia.org/wiki/PCX) properties
* [QuickTime](https://en.wikipedia.org/wiki/QuickTime_File_Format) properties
* [MP4](https://en.wikipedia.org/wiki/MPEG-4_Part_14) properties
It will process files of type:
* JPEG
* TIFF
* WebP
* WAV
* AVI
* PSD
* PNG
* BMP
* GIF
* ICO
* PCX
* QuickTime
* MP4
* Camera Raw
* NEF (Nikon)
* CR2 (Canon)
......@@ -117,6 +126,7 @@ Wherever possible, they have been credited in the source code and commit logs.
- .NET [metadata-extractor-dotnet](https://github.com/drewnoakes/metadata-extractor-dotnet) is a complete port to C#, maintained alongside this library
- PHP [php-metadata-extractor](https://github.com/gomoob/php-metadata-extractor) wraps this Java project, making it available to users of PHP
- Clojure [exif-processor](https://github.com/joshuamiller/exif-processor) wraps this Java project, returning a subset of data
---
......
......@@ -41,11 +41,6 @@ import java.util.Arrays;
*/
public class SampleUsage
{
/**
* Executes the sample usage program.
*
* @param args command line parameters
*/
public static void main(String[] args)
{
File file = new File("Tests/Data/withIptcExifGps.jpg");
......@@ -62,11 +57,11 @@ public class SampleUsage
try {
Metadata metadata = ImageMetadataReader.readMetadata(file);
print(metadata);
print(metadata, "Using ImageMetadataReader");
} catch (ImageProcessingException e) {
// handle exception
print(e);
} catch (IOException e) {
// handle exception
print(e);
}
//
......@@ -81,11 +76,11 @@ public class SampleUsage
try {
Metadata metadata = JpegMetadataReader.readMetadata(file);
print(metadata);
print(metadata, "Using JpegMetadataReader");
} catch (JpegProcessingException e) {
// handle exception
print(e);
} catch (IOException e) {
// handle exception
print(e);
}
//
......@@ -102,19 +97,25 @@ public class SampleUsage
Metadata metadata = JpegMetadataReader.readMetadata(file, readers);
print(metadata);
print(metadata, "Using JpegMetadataReader for Exif and IPTC only");
} catch (JpegProcessingException e) {
// handle exception
print(e);
} catch (IOException e) {
// handle exception
print(e);
}
}
private static void print(Metadata metadata)
/**
* Write all extracted values to stdout.
*/
private static void print(Metadata metadata, String method)
{
System.out.println("-------------------------------------");
// Iterate over the data and print to System.out
System.out.println();
System.out.println("-------------------------------------------------");
System.out.print(' ');
System.out.print(method);
System.out.println("-------------------------------------------------");
System.out.println();
//
// A Metadata object contains multiple Directory objects
......@@ -131,11 +132,14 @@ public class SampleUsage
//
// Each Directory may also contain error messages
//
if (directory.hasErrors()) {
for (String error : directory.getErrors()) {
System.err.println("ERROR: " + error);
}
}
}
private static void print(Exception exception)
{
System.err.println("EXCEPTION: " + exception);
}
}
......@@ -20,34 +20,107 @@
*/
package com.drew.imaging;
import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
/**
* Enumeration of supported image file formats.
* Enumeration of supported file types.
*
* MIME Type Source: https://www.freeformatter.com/mime-types-list.html
* https://www.iana.org/assignments/media-types/media-types.xhtml
*/
public enum FileType
{
Unknown,
Jpeg,
Tiff,
Psd,
Png,
Bmp,
Gif,
Ico,
Pcx,
Riff,
Unknown("Unknown", "Unknown", null),
Jpeg("JPEG", "Joint Photographic Experts Group", "image/jpeg", "jpg", "jpeg", "jpe"),
Tiff("TIFF", "Tagged Image File Format", "image/tiff", "tiff", "tif"),
Psd("PSD", "Photoshop Document", "image/vnd.adobe.photoshop", "psd"),
Png("PNG", "Portable Network Graphics", "image/png", "png"),
Bmp("BMP", "Device Independent Bitmap", "image/bmp", "bmp"),
Gif("GIF", "Graphics Interchange Format", "image/gif", "gif"),
Ico("ICO", "Windows Icon", "image/x-icon", "ico"),
Pcx("PCX", "PiCture eXchange", "image/x-pcx", "pcx"),
Riff("RIFF", "Resource Interchange File Format", null),
Wav("WAV", "Waveform Audio File Format", "audio/vnd.wave", "wav", "wave"),
Avi("AVI", "Audio Video Interleaved", "video/vnd.avi", "avi"),
WebP("WebP", "WebP", "image/webp", "webp"),
Mov("MOV", "QuickTime Movie", "video/quicktime", "mov", "qt"),
Mp4("MP4", "MPEG-4 Part 14", "video/mp4", "mp4", "m4a", "m4p", "m4b", "m4r", "m4v"),
Heif("HEIF", "High Efficiency Image File Format", "image/heif", "heif", "heic"),
Eps("EPS", "Encapsulated PostScript", "application/postscript", "eps", "epsf", "epsi"),
/** Sony camera raw. */
Arw,
Arw("ARW", "Sony Camera Raw", null, "arw"),
/** Canon camera raw, version 1. */
Crw,
Crw("CRW", "Canon Camera Raw", null, "crw"),
/** Canon camera raw, version 2. */
Cr2,
Cr2("CR2", "Canon Camera Raw", null, "cr2"),
/** Nikon camera raw. */
Nef,
Nef("NEF", "Nikon Camera Raw", null, "nef"),
/** Olympus camera raw. */
Orf,
Orf("ORF", "Olympus Camera Raw", null, "orf"),
/** FujiFilm camera raw. */
Raf,
Raf("RAF", "FujiFilm Camera Raw", null, "raf"),
/** Panasonic camera raw. */
Rw2
Rw2("RW2", "Panasonic Camera Raw", null, "rw2"),
// Only file detection
Aac("AAC", "Advanced Audio Coding", "audio/aac", "m4a"),
Asf("ASF", "Advanced Systems Format", "video/x-ms-asf", "asf", "wma", "wmv"),
Cfbf("CFBF", "Compound File Binary Format", null, null),
Flv("FLV", "Flash Video", "video/x-flv", ".flv", ".f4v,"),
Indd("INDD", "INDesign Document", "application/octet-stream", ".indd"),
Mxf("MXF", "Material Exchange Format", "application/mxf", "mxf"),
Pdf("PDF", "Portable Document Format", "application/pdf", "pdf"),
Qxp("QXP", "Quark XPress Document", null, "qzp", "qxd"),
Ram("RAM", "RealAudio", "audio/vnd.rn-realaudio", "aac", "ra"),
Rtf("RTF", "Rich Text Format", "application/rtf", "rtf"),
Sit("SIT", "Stuffit Archive", "application/x-stuffit", "sit"),
Sitx("SITX", "Stuffit X Archive", "application/x-stuffitx", "sitx"),
Swf("SWF", "Small Web Format", "application/vnd.adobe.flash-movie", "swf"),
Vob("VOB", "Video Object", "video/dvd", ".vob"),
Zip("ZIP", "ZIP Archive", "application/zip", ".zip", ".zipx");
@NotNull private final String _name;
@NotNull private final String _longName;
@Nullable private final String _mimeType;
private final String[] _extensions;
FileType(@NotNull String name, @NotNull String longName, @Nullable String mimeType, String... extensions)
{
_name = name;
_longName = longName;
_mimeType = mimeType;
_extensions = extensions;
}
@NotNull
public String getName()
{
return _name;
}
@NotNull
public String getLongName()
{
return _longName;
}
@Nullable
public String getMimeType()
{
return _mimeType;
}
@Nullable
public String getCommonExtension()
{
return (_extensions == null || _extensions.length == 0) ? null : _extensions[0];
}
@Nullable
public String[] getAllExtensions()
{
return _extensions;
}
}
......@@ -25,6 +25,7 @@ import com.drew.lang.annotations.NotNull;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.HashMap;
/**
* Examines the a file's first bytes and estimates the file's type.
......@@ -32,6 +33,7 @@ import java.io.IOException;
public class FileTypeDetector
{
private final static ByteTrie<FileType> _root;
private static final HashMap<String, FileType> _ftypMap;
static
{
......@@ -45,7 +47,12 @@ public class FileTypeDetector
_root.addPath(FileType.Tiff, "MM".getBytes(), new byte[]{0x00, 0x2a});
_root.addPath(FileType.Psd, "8BPS".getBytes());
_root.addPath(FileType.Png, new byte[]{(byte)0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52});
_root.addPath(FileType.Bmp, "BM".getBytes()); // TODO technically there are other very rare magic numbers for OS/2 BMP files...
_root.addPath(FileType.Bmp, "BM".getBytes()); // Standard Bitmap Windows and OS/2
_root.addPath(FileType.Bmp, "BA".getBytes()); // OS/2 Bitmap Array
_root.addPath(FileType.Bmp, "CI".getBytes()); // OS/2 Color Icon
_root.addPath(FileType.Bmp, "CP".getBytes()); // OS/2 Color Pointer
_root.addPath(FileType.Bmp, "IC".getBytes()); // OS/2 Icon
_root.addPath(FileType.Bmp, "PT".getBytes()); // OS/2 Pointer
_root.addPath(FileType.Gif, "GIF87a".getBytes());
_root.addPath(FileType.Gif, "GIF89a".getBytes());
_root.addPath(FileType.Ico, new byte[]{0x00, 0x00, 0x01, 0x00});
......@@ -54,16 +61,85 @@ public class FileTypeDetector
_root.addPath(FileType.Pcx, new byte[]{0x0A, 0x03, 0x01});
_root.addPath(FileType.Pcx, new byte[]{0x0A, 0x05, 0x01});
_root.addPath(FileType.Riff, "RIFF".getBytes());
_root.addPath(FileType.Arw, "II".getBytes(), new byte[]{0x2a, 0x00, 0x08, 0x00});
_root.addPath(FileType.Crw, "II".getBytes(), new byte[]{0x1a, 0x00, 0x00, 0x00}, "HEAPCCDR".getBytes());
_root.addPath(FileType.Cr2, "II".getBytes(), new byte[]{0x2a, 0x00, 0x10, 0x00, 0x00, 0x00, 0x43, 0x52});
_root.addPath(FileType.Nef, "MM".getBytes(), new byte[]{0x00, 0x2a, 0x00, 0x00, 0x00, (byte)0x80, 0x00});
// NOTE this doesn't work for NEF as it incorrectly flags many other TIFF files as being NEF
// _root.addPath(FileType.Nef, "MM".getBytes(), new byte[]{0x00, 0x2a, 0x00, 0x00, 0x00, (byte)0x08, 0x00});
_root.addPath(FileType.Orf, "IIRO".getBytes(), new byte[]{(byte)0x08, 0x00});
_root.addPath(FileType.Orf, "MMOR".getBytes(), new byte[]{(byte)0x00, 0x00});
_root.addPath(FileType.Orf, "IIRS".getBytes(), new byte[]{(byte)0x08, 0x00});
_root.addPath(FileType.Raf, "FUJIFILMCCD-RAW".getBytes());
_root.addPath(FileType.Rw2, "II".getBytes(), new byte[]{0x55, 0x00});
_root.addPath(FileType.Eps, "%!PS".getBytes());
_root.addPath(FileType.Eps, new byte[]{(byte)0xC5, (byte)0xD0, (byte)0xD3, (byte)0xC6});
_ftypMap = new HashMap<String, FileType>();
// http://www.ftyps.com
// QuickTime Mov
_ftypMap.put("ftypmoov", FileType.Mov);
_ftypMap.put("ftypwide", FileType.Mov);
_ftypMap.put("ftypmdat", FileType.Mov);
_ftypMap.put("ftypfree", FileType.Mov);
_ftypMap.put("ftypqt ", FileType.Mov);
// MP4
_ftypMap.put("ftypavc1", FileType.Mp4);
_ftypMap.put("ftypiso2", FileType.Mp4);
_ftypMap.put("ftypisom", FileType.Mp4);
_ftypMap.put("ftypM4A ", FileType.Mp4);
_ftypMap.put("ftypM4B ", FileType.Mp4);
_ftypMap.put("ftypM4P ", FileType.Mp4);
_ftypMap.put("ftypM4V ", FileType.Mp4);
_ftypMap.put("ftypM4VH", FileType.Mp4);
_ftypMap.put("ftypM4VP", FileType.Mp4);
_ftypMap.put("ftypmmp4", FileType.Mp4);
_ftypMap.put("ftypmp41", FileType.Mp4);
_ftypMap.put("ftypmp42", FileType.Mp4);
_ftypMap.put("ftypmp71", FileType.Mp4);
_ftypMap.put("ftypMSNV", FileType.Mp4);
_ftypMap.put("ftypNDAS", FileType.Mp4);
_ftypMap.put("ftypNDSC", FileType.Mp4);
_ftypMap.put("ftypNDSH", FileType.Mp4);
_ftypMap.put("ftypNDSM", FileType.Mp4);
_ftypMap.put("ftypNDSP", FileType.Mp4);
_ftypMap.put("ftypNDSS", FileType.Mp4);
_ftypMap.put("ftypNDXC", FileType.Mp4);
_ftypMap.put("ftypNDXH", FileType.Mp4);
_ftypMap.put("ftypNDXM", FileType.Mp4);
_ftypMap.put("ftypNDXP", FileType.Mp4);
_ftypMap.put("ftypNDXS", FileType.Mp4);
// HEIF
_ftypMap.put("ftypmif1", FileType.Heif);
_ftypMap.put("ftypmsf1", FileType.Heif);
_ftypMap.put("ftypheic", FileType.Heif);
_ftypMap.put("ftypheix", FileType.Heif);
_ftypMap.put("ftyphevc", FileType.Heif);
_ftypMap.put("ftyphevx", FileType.Heif);
// Only file detection
_root.addPath(FileType.Aac, new byte[]{(byte)0xFF, (byte)0xF1});
_root.addPath(FileType.Aac, new byte[]{(byte)0xFF, (byte)0xF9});
_root.addPath(FileType.Asf, new byte[]{0x30, 0x26, (byte)0xB2, 0x75, (byte)0x8E, 0x66, (byte)0xCF, 0x11, (byte)0xA6, (byte)0xD9, 0x00, (byte)0xAA, 0x00, 0x62, (byte)0xCE, 0x6C});
_root.addPath(FileType.Cfbf, new byte[]{(byte)0xD0, (byte)0xCF, 0x11, (byte)0xE0, (byte)0xA1, (byte)0xB1, 0x1A, (byte)0xE1, 0x00});
_root.addPath(FileType.Flv, new byte[]{0x46, 0x4C, 0x56});
_root.addPath(FileType.Indd, new byte[]{0x06, 0x06, (byte)0xED, (byte)0xF5, (byte)0xD8, 0x1D, 0x46, (byte)0xE5, (byte)0xBD, 0x31, (byte)0xEF, (byte)0xE7, (byte)0xFE, 0x74, (byte)0xB7, 0x1D});
_root.addPath(FileType.Mxf, new byte[]{0x06, 0x0e, 0x2b, 0x34, 0x02, 0x05, 0x01, 0x01, 0x0d, 0x01, 0x02, 0x01, 0x01, 0x02}); // has offset?
_root.addPath(FileType.Qxp, new byte[]{0x00, 0x00, 0x49, 0x49, 0x58, 0x50, 0x52, 0x33}); // "..IIXPR3" (little-endian - intel)
_root.addPath(FileType.Qxp, new byte[]{0x00, 0x00, 0x4D, 0x4D, 0x58, 0x50, 0x52, 0x33}); // "..MMXPR3" (big-endian - motorola)
_root.addPath(FileType.Ram, new byte[]{0x72, 0x74, 0x73, 0x70, 0x3A, 0x2F, 0x2F});
_root.addPath(FileType.Rtf, new byte[]{0x7B, 0x5C, 0x72, 0x74, 0x66, 0x31});
_root.addPath(FileType.Sit, new byte[]{ 0x53, 0x49, 0x54, 0x21, 0x00 }); // SIT!);
_root.addPath(FileType.Sit, new byte[]{ 0x53, 0x74, 0x75, 0x66, 0x66, 0x49, 0x74, 0x20, 0x28, 0x63, 0x29, 0x31, 0x39, 0x39, 0x37, 0x2D}); // StuffIt (c)1997-
_root.addPath(FileType.Sitx, new byte[]{ 0x53, 0x74, 0x75, 0x66, 0x66, 0x49, 0x74, 0x21 });
_root.addPath(FileType.Swf, "CWS".getBytes());
_root.addPath(FileType.Swf, "FWS".getBytes());
_root.addPath(FileType.Swf, "ZWS".getBytes());
_root.addPath(FileType.Vob, new byte[]{0x00, 0x00, 0x01, (byte)0xBA});
_root.addPath(FileType.Zip, "PK".getBytes());
}
private FileTypeDetector() throws Exception
......@@ -72,7 +148,7 @@ public class FileTypeDetector
}
/**
* Examines the a file's first bytes and estimates the file's type.
* Examines the file's bytes and estimates the file's type.
* <p>
* Requires a {@link BufferedInputStream} in order to mark and reset the stream to the position
* at which it was provided to this method once completed.
......@@ -87,7 +163,7 @@ public class FileTypeDetector
if (!inputStream.markSupported())
throw new IOException("Stream must support mark/reset");
int maxByteCount = _root.getMaxDepth();
int maxByteCount = Math.max(16, _root.getMaxDepth());
inputStream.mark(maxByteCount);
......@@ -99,7 +175,26 @@ public class FileTypeDetector
inputStream.reset();
//noinspection ConstantConditions
return _root.find(bytes);
FileType fileType = _root.find(bytes);
assert(fileType != null);
if (fileType == FileType.Unknown) {
String eightCC = new String(bytes, 4, 8);
// Test at offset 4 for Base Media Format (i.e. QuickTime, MP4, etc...) identifier "ftyp" plus four identifying characters
FileType t = _ftypMap.get(eightCC);
if (t != null)
return t;
} else if (fileType == FileType.Riff) {
String fourCC = new String(bytes, 8, 4);
if (fourCC.equals("WAVE"))
return FileType.Wav;
if (fourCC.equals("AVI "))
return FileType.Avi;
if (fourCC.equals("WEBP"))
return FileType.WebP;
}
return fileType;
}
}
......@@ -20,15 +20,20 @@
*/
package com.drew.imaging;
import com.drew.imaging.avi.AviMetadataReader;
import com.drew.imaging.bmp.BmpMetadataReader;
import com.drew.imaging.eps.EpsMetadataReader;
import com.drew.imaging.gif.GifMetadataReader;
import com.drew.imaging.ico.IcoMetadataReader;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.imaging.mp4.Mp4MetadataReader;
import com.drew.imaging.quicktime.QuickTimeMetadataReader;
import com.drew.imaging.pcx.PcxMetadataReader;
import com.drew.imaging.png.PngMetadataReader;
import com.drew.imaging.psd.PsdMetadataReader;
import com.drew.imaging.raf.RafMetadataReader;
import com.drew.imaging.tiff.TiffMetadataReader;
import com.drew.imaging.wav.WavMetadataReader;
import com.drew.imaging.webp.WebpMetadataReader;
import com.drew.lang.RandomAccessStreamReader;
import com.drew.lang.StringUtil;
......@@ -38,7 +43,8 @@ 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.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.file.FileTypeDirectory;
import java.io.*;
import java.util.ArrayList;
......@@ -53,16 +59,21 @@ import java.util.Collection;
* Parsing is then delegated to one of:
*
* <ul>
* <li>{@link JpegMetadataReader} for JPEG files</li>
* <li>{@link TiffMetadataReader} for TIFF and (most) RAW files</li>
* <li>{@link PsdMetadataReader} for Photoshop files</li>
* <li>{@link PngMetadataReader} for PNG files</li>
* <li>{@link AviMetadataReader} for AVI files</li>
* <li>{@link BmpMetadataReader} for BMP files</li>
* <li>{@link FileSystemMetadataReader} for metadata from the file system when a {@link File} is provided</li>
* <li>{@link GifMetadataReader} for GIF files</li>
* <li>{@link IcoMetadataReader} for ICO files</li>
* <li>{@link JpegMetadataReader} for JPEG files</li>
* <li>{@link Mp4MetadataReader} for MPEG-4 files</li>
* <li>{@link PcxMetadataReader} for PCX files</li>
* <li>{@link WebpMetadataReader} for WebP files</li>
* <li>{@link PngMetadataReader} for PNG files</li>
* <li>{@link PsdMetadataReader} for Photoshop files</li>
* <li>{@link QuickTimeMetadataReader} for QuickTime files</li>
* <li>{@link RafMetadataReader} for RAF files</li>
* <li>{@link TiffMetadataReader} for TIFF and (most) RAW files</li>
* <li>{@link WavMetadataReader} for WAV files</li>
* <li>{@link WebpMetadataReader} for WebP files</li>
* </ul>
*
* If you know the file type you're working with, you may use one of the above processors directly.
......@@ -107,42 +118,67 @@ public class ImageMetadataReader
FileType fileType = FileTypeDetector.detectFileType(bufferedInputStream);
if (fileType == FileType.Jpeg)
return JpegMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Tiff ||
fileType == FileType.Arw ||
fileType == FileType.Cr2 ||
fileType == FileType.Nef ||
fileType == FileType.Orf ||
fileType == FileType.Rw2)
return TiffMetadataReader.readMetadata(new RandomAccessStreamReader(bufferedInputStream, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, streamLength));
if (fileType == FileType.Psd)
return PsdMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Png)
return PngMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Bmp)
return BmpMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Gif)
return GifMetadataReader.readMetadata(bufferedInputStream);
Metadata metadata = readMetadata(bufferedInputStream, streamLength, fileType);
if (fileType == FileType.Ico)
return IcoMetadataReader.readMetadata(bufferedInputStream);
metadata.addDirectory(new FileTypeDirectory(fileType));
if (fileType == FileType.Pcx)
return PcxMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Riff)
return WebpMetadataReader.readMetadata(bufferedInputStream);
if (fileType == FileType.Raf)
return RafMetadataReader.readMetadata(bufferedInputStream);
return metadata;
}
throw new ImageProcessingException("File format is not supported");
/**
* Reads metadata from an {@link InputStream} of known length and file type.
*
* @param inputStream a stream from which the file data may be read. The stream must be positioned at the
* beginning of the file's data.
* @param streamLength the length of the stream, if known, otherwise -1.
* @param fileType the file type of the data stream.
* @return a populated {@link Metadata} object containing directories of tags with values and any processing errors.
* @throws ImageProcessingException if the file type is unknown, or for general processing errors.
*/
@NotNull
public static Metadata readMetadata(@NotNull final InputStream inputStream, final long streamLength, final FileType fileType) throws IOException, ImageProcessingException
{
switch (fileType) {
case Jpeg:
return JpegMetadataReader.readMetadata(inputStream);
case Tiff:
case Arw:
case Cr2:
case Nef:
case Orf:
case Rw2:
return TiffMetadataReader.readMetadata(new RandomAccessStreamReader(inputStream, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, streamLength));
case Psd:
return PsdMetadataReader.readMetadata(inputStream);
case Png:
return PngMetadataReader.readMetadata(inputStream);
case Bmp:
return BmpMetadataReader.readMetadata(inputStream);
case Gif:
return GifMetadataReader.readMetadata(inputStream);
case Ico:
return IcoMetadataReader.readMetadata(inputStream);
case Pcx:
return PcxMetadataReader.readMetadata(inputStream);
case WebP:
return WebpMetadataReader.readMetadata(inputStream);
case Raf:
return RafMetadataReader.readMetadata(inputStream);
case Avi:
return AviMetadataReader.readMetadata(inputStream);
case Wav:
return WavMetadataReader.readMetadata(inputStream);
case Mov:
return QuickTimeMetadataReader.readMetadata(inputStream);
case Mp4:
return Mp4MetadataReader.readMetadata(inputStream);
case Eps:
return EpsMetadataReader.readMetadata(inputStream);
case Unknown:
throw new ImageProcessingException("File format could not be determined");
default:
return new Metadata();
}
}
/**
......@@ -162,7 +198,7 @@ public class ImageMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......
/*
* Copyright 2002-2017 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:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.imaging.avi;
import com.drew.imaging.riff.RiffProcessingException;
import com.drew.imaging.riff.RiffReader;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.avi.AviRiffHandler;
import com.drew.metadata.file.FileSystemMetadataReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Obtains metadata from AVI files.
*
* @author Payton Garland
*/
public class AviMetadataReader
{
@NotNull
public static Metadata readMetadata(@NotNull File file) throws IOException, RiffProcessingException
{
InputStream inputStream = new FileInputStream(file);
Metadata metadata;
try {
metadata = readMetadata(inputStream);
} finally {
inputStream.close();
}
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
@NotNull
public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException, RiffProcessingException
{
Metadata metadata = new Metadata();
new RiffReader().processRiff(new StreamReader(inputStream), new AviRiffHandler(metadata));
return metadata;
}
}
/**
* Contains classes for working with AVI files.
*/
package com.drew.imaging.avi;
......@@ -25,6 +25,7 @@ import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.bmp.BmpReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import java.io.*;
......@@ -38,15 +39,15 @@ public class BmpMetadataReader
@NotNull
public static Metadata readMetadata(@NotNull File file) throws IOException
{
FileInputStream stream = null;
InputStream inputStream = new FileInputStream(file);
Metadata metadata;
try {
stream = new FileInputStream(file);
return readMetadata(stream);
metadata = readMetadata(inputStream);
} finally {
if (stream != null) {
stream.close();
}
inputStream.close();
}
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
@NotNull
......
package com.drew.imaging.eps;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.eps.EpsReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Obtains metadata from EPS files.
*
* @author Payton Garland
*/
public class EpsMetadataReader {
@NotNull
public static Metadata readMetadata(@NotNull File file) throws IOException
{
Metadata metadata = new Metadata();
new EpsReader().extract(new FileInputStream(file), metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
@NotNull
public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException
{
Metadata metadata = new Metadata();
new EpsReader().extract(inputStream, metadata);
return metadata;
}
}
/**
* Contains classes for working with EPS files.
*/
package com.drew.imaging.eps;
......@@ -24,7 +24,7 @@ package com.drew.imaging.gif;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.gif.GifReader;
import java.io.File;
......@@ -49,7 +49,7 @@ public class GifMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......
......@@ -23,7 +23,7 @@ package com.drew.imaging.ico;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.ico.IcoReader;
import java.io.*;
......@@ -45,7 +45,7 @@ public class IcoMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......
......@@ -26,7 +26,7 @@ import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Metadata;
import com.drew.metadata.adobe.AdobeJpegReader;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.icc.IccReader;
import com.drew.metadata.iptc.IptcReader;
import com.drew.metadata.jfif.JfifReader;
......@@ -94,7 +94,7 @@ public class JpegMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......
/*
* Copyright 2002-2017 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:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.imaging.mp4;
import com.drew.lang.annotations.NotNull;
import com.drew.lang.annotations.Nullable;
import com.drew.metadata.Metadata;
import com.drew.metadata.mp4.Mp4Directory;
import com.drew.metadata.mp4.boxes.Box;
import java.io.IOException;
/**
* @author Payton Garland
*/
public abstract class Mp4Handler<T extends Mp4Directory>
{
@NotNull protected Metadata metadata;
@NotNull protected T directory;
public Mp4Handler(@NotNull Metadata metadata)
{
this.metadata = metadata;
this.directory = getDirectory();
metadata.addDirectory(directory);
}
@NotNull
protected abstract T getDirectory();
protected abstract boolean shouldAcceptBox(@NotNull Box box);
protected abstract boolean shouldAcceptContainer(@NotNull Box box);
protected abstract Mp4Handler processBox(@NotNull Box box, @Nullable byte[] payload) throws IOException;
protected Mp4Handler processContainer(@NotNull Box box) throws IOException
{
return processBox(box, null);
}
public void addError(@NotNull String message)
{
directory.addError(message);
}
}
/*
* Copyright 2002-2017 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:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.imaging.mp4;
import com.drew.imaging.ImageProcessingException;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.mp4.Mp4BoxHandler;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Payton Garland
*/
public class Mp4MetadataReader
{
@NotNull
public static Metadata readMetadata(@NotNull final File file) throws ImageProcessingException, IOException
{
InputStream inputStream = new FileInputStream(file);
Metadata metadata;
try {
metadata = readMetadata(inputStream);
} finally {
inputStream.close();
}
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
@NotNull
public static Metadata readMetadata(@NotNull InputStream inputStream) throws IOException
{
Metadata metadata = new Metadata();
Mp4Reader.extract(inputStream, new Mp4BoxHandler(metadata));
return metadata;
}
}
/*
* Copyright 2002-2017 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:
*
* https://drewnoakes.com/code/exif/
* https://github.com/drewnoakes/metadata-extractor
*/
package com.drew.imaging.mp4;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.mp4.boxes.Box;
import java.io.IOException;
import java.io.InputStream;
/**
* @author Payton Garland
*/
public class Mp4Reader
{
private Mp4Reader() {}
public static void extract(@NotNull InputStream inputStream, @NotNull Mp4Handler handler)
{
StreamReader reader = new StreamReader(inputStream);
reader.setMotorolaByteOrder(true);
processBoxes(reader, -1, handler);
}
private static void processBoxes(StreamReader reader, long atomEnd, Mp4Handler handler)
{
try {
while (atomEnd == -1 || reader.getPosition() < atomEnd) {
Box box = new Box(reader);
// Determine if fourCC is container/atom and process accordingly.
// Unknown atoms will be skipped
if (handler.shouldAcceptContainer(box)) {
processBoxes(reader, box.size + reader.getPosition() - 8, handler.processContainer(box));
} else if (handler.shouldAcceptBox(box)) {
handler = handler.processBox(box, reader.getBytes((int)box.size - 8));
} else if (box.usertype != null) {
reader.skip(box.size - 24);
} else if (box.size > 1) {
reader.skip(box.size - 8);
} else if (box.size == -1) {
break;
}
}
} catch (IOException e) {
handler.addError(e.getMessage());
}
}
}
......@@ -23,7 +23,7 @@ package com.drew.imaging.pcx;
import com.drew.lang.StreamReader;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.pcx.PcxReader;
import java.io.*;
......@@ -45,7 +45,7 @@ public class PcxMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......
......@@ -86,6 +86,9 @@ public class PngChunkReader
// Process the next chunk.
int chunkDataLength = reader.getInt32();
if (chunkDataLength < 0)
throw new PngProcessingException("PNG chunk length exceeds maximum");
PngChunkType chunkType = new PngChunkType(reader.getBytes(4));
boolean willStoreChunk = desiredChunkTypes == null || desiredChunkTypes.contains(chunkType);
......
......@@ -24,7 +24,7 @@ import com.drew.lang.*;
import com.drew.lang.annotations.NotNull;
import com.drew.metadata.Metadata;
import com.drew.metadata.StringValue;
import com.drew.metadata.file.FileMetadataReader;
import com.drew.metadata.file.FileSystemMetadataReader;
import com.drew.metadata.icc.IccReader;
import com.drew.metadata.png.PngChromaticitiesDirectory;
import com.drew.metadata.png.PngDirectory;
......@@ -87,7 +87,7 @@ public class PngMetadataReader
} finally {
inputStream.close();
}
new FileMetadataReader().read(file, metadata);
new FileSystemMetadataReader().read(file, metadata);
return metadata;
}
......