📄 exifreader.java
字号:
/*
* EXIFExtractor.java
*
* This class based upon code from Jhead, a C program for extracting and
* manipulating the Exif data within files written by Matthias Wandel.
* http://www.sentex.net/~mwandel/jhead/
*
* Jhead is public domain software - that is, you can do whatever you want
* with it, and include it software that is licensed under the GNU or the
* BSD license, or whatever other licence you choose, including proprietary
* closed source licenses. Similarly, I release this Java version under the
* same license, though I do ask that you leave this header in tact.
*
* If you make modifications to this code that you think would benefit the
* wider community, please send me a copy and I'll post it on my site. Unlike
* Jhead, this code (as it stands) only supports reading of Exif data - no
* manipulation, and no thumbnail stuff.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew.noakes@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created on 28 April 2002, 23:54
* Modified 04 Aug 2002
* - Renamed constants to be inline with changes to ExifTagValues interface
* - Substituted usage of JDK 1.4 features (java.nio package)
* Modified 29 Oct 2002 (v1.2)
* - Proper traversing of Exif file structure and complete refactor & tidy of
* the codebase (a few unnoticed bugs removed)
* - Reads makernote data for 6 families of camera (5 makes)
* - Tags now stored in directories... use the IFD_* constants to refer to the
* image file directory you require (Exif, Interop, GPS and Makernote*) --
* this avoids collisions where two tags share the same code
* - Takes componentCount of unknown tags into account
* - Now understands GPS tags (thanks to Colin Briton for his help with this)
* - Some other bug fixes, pointed out by users around the world. Thanks!
* Modified 27 Nov 2002 (v2.0)
* - Renamed to ExifReader
* - Moved to new package com.drew.metadata.exif
* Modified since, however changes have not been logged. See release notes for
* library-wide modifications.
*/
package com.drew.metadata.exif;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.imaging.jpeg.JpegSegmentData;
import com.drew.imaging.jpeg.JpegSegmentReader;
import com.drew.lang.Rational;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.MetadataReader;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
/**
* Extracts Exif data from a JPEG header segment, providing information about the
* camera/scanner/capture device (if available). Information is encapsulated in
* an <code>Metadata</code> object.
* @author Drew Noakes http://drewnoakes.com
*/
public class ExifReader implements MetadataReader
{
/**
* The JPEG segment as an array of bytes.
*/
private final byte[] _data;
/**
* Represents the native byte ordering used in the JPEG segment. If true,
* then we're using Motorolla ordering (Big endian), else we're using Intel
* ordering (Little endian).
*/
private boolean _isMotorollaByteOrder;
/**
* Bean instance to store information about the image and camera/scanner/capture
* device.
*/
private Metadata _metadata;
/**
* The number of bytes used per format descriptor.
*/
private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
/**
* The number of formats known.
*/
private static final int MAX_FORMAT_CODE = 12;
// Format types
// Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.
// Is there a better way?
private static final int FMT_BYTE = 1;
private static final int FMT_STRING = 2;
private static final int FMT_USHORT = 3;
private static final int FMT_ULONG = 4;
private static final int FMT_URATIONAL = 5;
private static final int FMT_SBYTE = 6;
private static final int FMT_UNDEFINED = 7;
private static final int FMT_SSHORT = 8;
private static final int FMT_SLONG = 9;
private static final int FMT_SRATIONAL = 10;
private static final int FMT_SINGLE = 11;
private static final int FMT_DOUBLE = 12;
public static final int TAG_EXIF_OFFSET = 0x8769;
public static final int TAG_INTEROP_OFFSET = 0xA005;
public static final int TAG_GPS_INFO_OFFSET = 0x8825;
public static final int TAG_MAKER_NOTE = 0x927C;
public static final int TIFF_HEADER_START_OFFSET = 6;
/**
* Creates an ExifReader for a JpegSegmentData object.
* @param segmentData
*/
public ExifReader(JpegSegmentData segmentData)
{
this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1));
}
/**
* Creates an ExifReader for a Jpeg file.
* @param file
* @throws JpegProcessingException
*/
public ExifReader(File file) throws JpegProcessingException
{
this(new JpegSegmentReader(file).readSegment(JpegSegmentReader.SEGMENT_APP1));
}
/**
* Creates an ExifReader for a Jpeg stream.
* @param is JPEG stream. Stream will be closed.
*/
public ExifReader(InputStream is) throws JpegProcessingException
{
this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APP1));
}
/**
* Creates an ExifReader for the given JPEG header segment.
*/
public ExifReader(byte[] data)
{
_data = data;
}
/**
* Performs the Exif data extraction, returning a new instance of <code>Metadata</code>.
*/
public Metadata extract()
{
return extract(new Metadata());
}
/**
* Performs the Exif data extraction, adding found values to the specified
* instance of <code>Metadata</code>.
*/
public Metadata extract(Metadata metadata)
{
_metadata = metadata;
if (_data==null)
return _metadata;
// once we know there's some data, create the directory and start working on it
ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class);
// check for the header length
if (_data.length<=14) {
directory.addError("Exif data segment must contain at least 14 bytes");
return _metadata;
}
// check for the header preamble
if (!"Exif\0\0".equals(new String(_data, 0, 6))) {
directory.addError("Exif data segment doesn't begin with 'Exif'");
return _metadata;
}
// this should be either "MM" or "II"
String byteOrderIdentifier = new String(_data, 6, 2);
if (!setByteOrder(byteOrderIdentifier)) {
directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
return _metadata;
}
// Check the next two values for correctness.
if (get16Bits(8)!=0x2a) {
directory.addError("Invalid Exif start - should have 0x2A at offset 8 in Exif header");
return _metadata;
}
int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;
// David Ekholm sent an digital camera image that has this problem
if (firstDirectoryOffset>=_data.length - 1) {
directory.addError("First exif directory offset is beyond end of Exif data segment");
// First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
firstDirectoryOffset = 14;
}
HashMap processedDirectoryOffsets = new HashMap();
// 0th IFD (we merge with Exif IFD)
processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);
// after the extraction process, if we have the correct tags, we may be able to store thumbnail information
storeThumbnailBytes(directory, TIFF_HEADER_START_OFFSET);
return _metadata;
}
private void storeThumbnailBytes(ExifDirectory exifDirectory, int tiffHeaderOffset)
{
if (!exifDirectory.containsTag(ExifDirectory.TAG_COMPRESSION))
return;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -