libmetadata-extractor-java-2.3.1/ 0000755 0000000 0000000 00000000000 10716136547 015424 5 ustar root root libmetadata-extractor-java-2.3.1/META-INF/ 0000755 0000000 0000000 00000000000 10716136374 016562 5 ustar root root libmetadata-extractor-java-2.3.1/META-INF/MANIFEST.MF 0000644 0000000 0000000 00000000153 10716136374 020213 0 ustar root root Manifest-Version: 1.0
Ant-Version: Apache Ant 1.5.4
Created-By: 1.4.2_03-b02 (Sun Microsystems Inc.)
libmetadata-extractor-java-2.3.1/build.xml 0000644 0000000 0000000 00000006130 10716136374 017243 0 ustar root root
metadata-extractor for java build file
Copyright © 2006 Drew Noakes. All Rights Reserved.]]>
libmetadata-extractor-java-2.3.1/ChangeLog.txt 0000644 0000000 0000000 00000017144 10716136374 020021 0 ustar root root v2.3.1 - 25 Feb 2006
-------------------------------
- Fixed copy-and-paste errors in ExifDescriptor.java. Thanks to Ferret Renaud.
v2.3.0 - 12 Jan 2006
-------------------------------
- New tags from Exif 2.2 specification (A401-A420).
- Fixed stack overflow exception in ExifReader for cyclic directory references.
Thanks to John Sidney-Woollett for reporting this bug (reported for Fuji FinePix A101 and Canon 20D).
- Fixed rounding error in the shutter speed description which was giving the wrong value most of the
time (for example, 1/32 instead of 1/50).
Thanks for Gli Blr and Mark Edwards for pointing out this error.
- Fix thread safety bug in ExifReader.
- Fixed OutOfMemoryError seen in certain Canon 20D images.
Thanks to Henry Yeung for providing an image to reproduce this error.
- Support for Windows XP Exif tags (Author, title, comments, etc).
- Added more documentation, and removed commented/unused code.
- Enhanced descriptor support for Exif tags.
- Extract comments in non-ASCII encodings.
- Improved camera model MakerNote support:
- New models:
- Epson (thanks to David Carlson for pointing me in the right direction with this)
- Kyocera / Contax (very limited)
- Minolta (it utilises the Olympus format)
- Panasonic
- Pentax / Asahi
- Improved support for models:
- Olympus
- Canon (tested with newer Canon models, including the 20D)
- Casio (for more modern models)
- Source distribution filesize reduced by using .metadata files, rather than entire sample JPEG files.
These .metadata files contain all non-image JPEG segments, making them suitable for unit tests whilst
being much smaller on disk.
v2.2.2 - 22 Nov 2003
-------------------------------
- Fixed a bug where version strings were assumed to be comprised of exactly four parts,
and cases were found where a different number existed
v2.2.1 - 24 Oct 2003
-------------------------------
- Fixed a bug where JpegDirectory had tag names for image width and height around
the wrong way. Thanks to Sander Smith for pointing this out
v2.2 - 18 Oct 2003
-------------------------------
- Added support for extraction of Jpeg image information (from the SOF0 segment)
Thanks to Darrell Silver for commencing the code for this extension
- Added support for reading Jpeg comments
- Additional Nikon camera makernote support for D1/D100 family models
Thanks to Daniel Waeber for providing sample images and to Fabrizio Giudici for
publishing his work in decoding this makernote data
- Added convenient writing of thumbnails to files from ExifDirectory
- Fixed a bug in date format strings, whereby times in the AM / PM were indistinguishable
Thanks to Bill Boland for being the first person to point this out (this was a popular one!)
- Fixed bug for multi-component tag values of certain types
Thanks to Derek Wegner for identifying the bug and providing a solution
- More unit tests (consequently, the source-code download is much larger)
- First version with an Ant build script
v2.1 - 12 Jan 2003
-------------------------------
- Extract methods no longer throw exceptions, with error information stored
in Metadata instances, using hasErrors() and getErrors()
- Metadata and dependant classes now serializable for network transmission,
and persistance in files & databases
- Support for extracting metadata from InputStreams, such as network connections
- Replaced code that depended upon JDK 1.4
v2.0 - 10 Dec 2002
-------------------------------
Enormous changes to the class and package structure in this release prohibit a
class-by-class breakdown of changes. The focus is no longer on Exif metadata
alone, but now on general metadata extraction from multiple media types.
Changes support:
- easier future extensibility
- Iptc metadata extraction
- multiple directories of tags
- descriptor class for interpreting values in a given directory
- multiple media and metadata types
- enhanced handling of exif makernote values
- many more unit tests
- numerous enhancements
- minor bug fixes
Simpler extensibility changes the focus from exif extraction alone and opens
the scope to general metadata extraction. Future development will introduce
new media and metadata support with little or no impact to existing classes.
v1.2 - 6 Nov 2002
-------------------------------
ExifExtractor.java
- 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
- Correct extraction of multi-component values
- No longer decodes image to extract Exif data -- this is much faster
- Takes componentCount of unknown tags into account
- Now understands GPS tags (thanks to Colin Briton for his help with this)
- Returns null when no Exif data present, instead of throwing an exception
- Some other bug fixes, pointed out by users around the world. Thanks!
ExifLoader
- Removed (unnecessary)
ImageInfo.java
- Stored IFD directories in separate tag-spaces
- iterator() now returns an Iterator over a list of TagValue objects
- More get*Description() methods to detail GPS tags, among others
TagValue.java
- New class to encapsualte information about a particular tag
Rational.java
- Improved toSimpleString() to factor more complex rational numbers into
a simpler form
i.e.
10/15 -> 2/3
- toSimpleString() now accepts a boolean flag, 'allowDecimals' which will
display the rational number in decimal form if it fits within 5 digits
i.e.
3/4 -> 0.75 when allowDecimal == true
JpegSegmentReader
- New class to extract APP1 segment (and others) from a Jpeg file -- this
avoids decoding images just to get metadata
tests\*.java
- First collection of basic unit tests, to compile against JUnit
- Doesn't yet cover all classes
Website
- A collection of JPEGs from various digital camera models, collected on
the web and contributed by many users of ExifExtractor
- Updated documentation
v1.1.1
-------------------------------
Rational.java
- Added toSimpleString() method, which returns a simplified and hopefully
more readable version of the Rational
i.e.
2/10 -> 1/5
10/2 -> 5
ExifExtractor.java
- Removed unnecessary casting operations
- Added a few more comments
- Removed redundant and commented code (I'm using a CVS system now)
ExifLoader.java
- Added a much-needed close() call to a created input stream, allowing
continued use of the File object passed to ExifLoader.getImageInfo(File)
ImageInfo.java
- Make use of new Rational method toSimpleString() for more elegant output -
Use of DecimalFormatter to tidy output in selected get***Description() methods
v1.1.0 - 28 Aug 2002
-------------------------------
- Descriptive tag values, including units and text for enumerations
- Decoupling from JDK 1.4-specific libraries (tested with JDK 1.3)
- More complete list of tags, both as constants for direct lookup, and via the
static lookup method
v1.0
-------------------------------
- Initial release libmetadata-extractor-java-2.3.1/Libraries/ 0000755 0000000 0000000 00000000000 10716136555 017337 5 ustar root root libmetadata-extractor-java-2.3.1/src/ 0000755 0000000 0000000 00000000000 10716136374 016211 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/ 0000755 0000000 0000000 00000000000 10716136374 016767 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/drew/ 0000755 0000000 0000000 00000000000 10716136374 017730 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/drew/imaging/ 0000755 0000000 0000000 00000000000 10716136374 021343 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/ 0000755 0000000 0000000 00000000000 10716136374 022270 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/JpegMetadataReader.java 0000644 0000000 0000000 00000015275 10716136374 026616 0 ustar root root /*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created by dnoakes on 12-Nov-2002 18:51:36 using IntelliJ IDEA.
*/
package com.drew.imaging.jpeg;
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.ExifDirectory;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.iptc.IptcReader;
import com.drew.metadata.jpeg.JpegCommentReader;
import com.drew.metadata.jpeg.JpegReader;
import com.sun.image.codec.jpeg.JPEGDecodeParam;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
/**
*
*/
public class JpegMetadataReader
{
// public static Metadata readMetadata(IIOMetadata metadata) throws JpegProcessingException {}
// public static Metadata readMetadata(ImageInputStream in) throws JpegProcessingException{}
// public static Metadata readMetadata(IIOImage image) throws JpegProcessingException{}
// public static Metadata readMetadata(ImageReader reader) throws JpegProcessingException{}
public static Metadata readMetadata(InputStream in) throws JpegProcessingException
{
JpegSegmentReader segmentReader = new JpegSegmentReader(in);
return extractMetadataFromJpegSegmentReader(segmentReader);
}
public static Metadata readMetadata(File file) throws JpegProcessingException
{
JpegSegmentReader segmentReader = new JpegSegmentReader(file);
return extractMetadataFromJpegSegmentReader(segmentReader);
}
public static Metadata extractMetadataFromJpegSegmentReader(JpegSegmentReader segmentReader)
{
final Metadata metadata = new Metadata();
try {
byte[] exifSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1);
new ExifReader(exifSegment).extract(metadata);
} catch (JpegProcessingException e) {
// in the interests of catching as much data as possible, continue
// TODO lodge error message within exif directory?
}
try {
byte[] iptcSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APPD);
new IptcReader(iptcSegment).extract(metadata);
} catch (JpegProcessingException e) {
// TODO lodge error message within iptc directory?
}
try {
byte[] jpegSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_SOF0);
new JpegReader(jpegSegment).extract(metadata);
} catch (JpegProcessingException e) {
// TODO lodge error message within jpeg directory?
}
try {
byte[] jpegCommentSegment = segmentReader.readSegment(JpegSegmentReader.SEGMENT_COM);
new JpegCommentReader(jpegCommentSegment).extract(metadata);
} catch (JpegProcessingException e) {
// TODO lodge error message within jpegcomment directory?
}
return metadata;
}
public static Metadata readMetadata(JPEGDecodeParam decodeParam)
{
final Metadata metadata = new Metadata();
/* We should only really be seeing Exif in _data[0]... the 2D array exists
* because markers can theoretically appear multiple times in the file.
*/
// TODO test this method
byte[][] exifSegment = decodeParam.getMarkerData(JPEGDecodeParam.APP1_MARKER);
if (exifSegment != null && exifSegment[0].length>0) {
new ExifReader(exifSegment[0]).extract(metadata);
}
// similarly, use only the first IPTC segment
byte[][] iptcSegment = decodeParam.getMarkerData(JPEGDecodeParam.APPD_MARKER);
if (iptcSegment != null && iptcSegment[0].length>0) {
new IptcReader(iptcSegment[0]).extract(metadata);
}
// NOTE: Unable to utilise JpegReader for the SOF0 frame here, as the decodeParam doesn't contain the byte[]
// similarly, use only the first Jpeg Comment segment
byte[][] jpegCommentSegment = decodeParam.getMarkerData(JPEGDecodeParam.COMMENT_MARKER);
if (jpegCommentSegment != null && jpegCommentSegment[0].length>0) {
new JpegCommentReader(jpegCommentSegment[0]).extract(metadata);
}
return metadata;
}
private JpegMetadataReader()
{
}
public static void main(String[] args) throws MetadataException, IOException
{
Metadata metadata = null;
try {
metadata = JpegMetadataReader.readMetadata(new File(args[0]));
} catch (Exception e) {
e.printStackTrace(System.err);
System.exit(1);
}
// iterate over the exif data and print to System.out
Iterator directories = metadata.getDirectoryIterator();
while (directories.hasNext()) {
Directory directory = (Directory)directories.next();
Iterator tags = directory.getTagIterator();
while (tags.hasNext()) {
Tag tag = (Tag)tags.next();
try {
System.out.println("[" + directory.getName() + "] " + tag.getTagName() + " = " + tag.getDescription());
} catch (MetadataException e) {
System.err.println(e.getMessage());
System.err.println(tag.getDirectoryName() + " " + tag.getTagName() + " (error)");
}
}
if (directory.hasErrors()) {
Iterator errors = directory.getErrors();
while (errors.hasNext()) {
System.out.println("ERROR: " + errors.next());
}
}
}
if (args.length>1 && args[1].trim().equals("/thumb"))
{
ExifDirectory directory = (ExifDirectory)metadata.getDirectory(ExifDirectory.class);
if (directory.containsThumbnail())
{
System.out.println("Writing thumbnail...");
directory.writeThumbnail(args[0].trim() + ".thumb.jpg");
}
else
{
System.out.println("No thumbnail data exists in this image");
}
}
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/JpegProcessingException.java 0000644 0000000 0000000 00000002510 10716136374 027732 0 ustar root root /*
* JpegProcessingException.java
*
* This class 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created by dnoakes on 04-Nov-2002 19:31:29 using IntelliJ IDEA.
*/
package com.drew.imaging.jpeg;
import com.drew.lang.CompoundException;
/**
* An exception class thrown upon unexpected and fatal conditions while processing
* a Jpeg file.
* @author Drew Noakes http://drewnoakes.com
*/
public class JpegProcessingException extends CompoundException
{
public JpegProcessingException(String message)
{
super(message);
}
public JpegProcessingException(String message, Throwable cause)
{
super(message, cause);
}
public JpegProcessingException(Throwable cause)
{
super(cause);
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/JpegSegmentData.java 0000644 0000000 0000000 00000010017 10716136374 026134 0 ustar root root /*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*/
package com.drew.imaging.jpeg;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Holds a collection of Jpeg data segments. This need not necessarily be all segments
* within the Jpeg. For example, it may be convenient to port about only the non-image
* segments when analysing (or serializing) metadata.
*/
public class JpegSegmentData implements Serializable
{
static final long serialVersionUID = 7110175216435025451L;
/** A map of byte[], keyed by the segment marker */
private final HashMap _segmentDataMap;
public JpegSegmentData()
{
_segmentDataMap = new HashMap(10);
}
public void addSegment(byte segmentMarker, byte[] segmentBytes)
{
List segmentList = getOrCreateSegmentList(segmentMarker);
segmentList.add(segmentBytes);
}
public byte[] getSegment(byte segmentMarker)
{
return getSegment(segmentMarker, 0);
}
public byte[] getSegment(byte segmentMarker, int occurrence)
{
final List segmentList = getSegmentList(segmentMarker);
if (segmentList==null || segmentList.size()<=occurrence)
return null;
else
return (byte[]) segmentList.get(occurrence);
}
public int getSegmentCount(byte segmentMarker)
{
final List segmentList = getSegmentList(segmentMarker);
if (segmentList==null)
return 0;
else
return segmentList.size();
}
public void removeSegmentOccurrence(byte segmentMarker, int occurrence)
{
final List segmentList = (List)_segmentDataMap.get(new Byte(segmentMarker));
segmentList.remove(occurrence);
}
public void removeSegment(byte segmentMarker)
{
_segmentDataMap.remove(new Byte(segmentMarker));
}
private List getSegmentList(byte segmentMarker)
{
return (List)_segmentDataMap.get(new Byte(segmentMarker));
}
private List getOrCreateSegmentList(byte segmentMarker)
{
List segmentList;
Byte key = new Byte(segmentMarker);
if (_segmentDataMap.containsKey(key)) {
segmentList = (List)_segmentDataMap.get(key);
} else {
segmentList = new ArrayList();
_segmentDataMap.put(key, segmentList);
}
return segmentList;
}
public boolean containsSegment(byte segmentMarker)
{
return _segmentDataMap.containsKey(new Byte(segmentMarker));
}
public static void ToFile(File file, JpegSegmentData segmentData) throws IOException
{
ObjectOutputStream outputStream = null;
try
{
outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(segmentData);
}
finally
{
if (outputStream!=null)
outputStream.close();
}
}
public static JpegSegmentData FromFile(File file) throws IOException, ClassNotFoundException
{
ObjectInputStream inputStream = null;
try
{
inputStream = new ObjectInputStream(new FileInputStream(file));
return (JpegSegmentData)inputStream.readObject();
}
finally
{
if (inputStream!=null)
inputStream.close();
}
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/JpegSegmentReader.java 0000644 0000000 0000000 00000026352 10716136374 026476 0 ustar root root /*
* JpegSegmentReader.java
*
* This class written by Drew Noakes, in accordance with the Jpeg specification.
*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA
*/
package com.drew.imaging.jpeg;
import java.io.*;
/**
* Performs read functions of Jpeg files, returning specific file segments.
* TODO add a findAvailableSegments() method
* TODO add more segment identifiers
* TODO add a getSegmentDescription() method, returning for example 'App1 application data segment, commonly containing Exif data'
* @author Drew Noakes http://drewnoakes.com
*/
public class JpegSegmentReader
{
// Jpeg data can be sourced from either a file, byte[] or InputStream
/** Jpeg file */
private final File _file;
/** Jpeg data as byte array */
private final byte[] _data;
/** Jpeg data as an InputStream */
private final InputStream _stream;
private JpegSegmentData _segmentData;
/**
* Private, because this segment crashes my algorithm, and searching for
* it doesn't work (yet).
*/
private static final byte SEGMENT_SOS = (byte)0xDA;
/**
* Private, because one wouldn't search for it.
*/
private static final byte MARKER_EOI = (byte)0xD9;
/** APP0 Jpeg segment identifier -- Jfif data. */
public static final byte SEGMENT_APP0 = (byte)0xE0;
/** APP1 Jpeg segment identifier -- where Exif data is kept. */
public static final byte SEGMENT_APP1 = (byte)0xE1;
/** APP2 Jpeg segment identifier. */
public static final byte SEGMENT_APP2 = (byte)0xE2;
/** APP3 Jpeg segment identifier. */
public static final byte SEGMENT_APP3 = (byte)0xE3;
/** APP4 Jpeg segment identifier. */
public static final byte SEGMENT_APP4 = (byte)0xE4;
/** APP5 Jpeg segment identifier. */
public static final byte SEGMENT_APP5 = (byte)0xE5;
/** APP6 Jpeg segment identifier. */
public static final byte SEGMENT_APP6 = (byte)0xE6;
/** APP7 Jpeg segment identifier. */
public static final byte SEGMENT_APP7 = (byte)0xE7;
/** APP8 Jpeg segment identifier. */
public static final byte SEGMENT_APP8 = (byte)0xE8;
/** APP9 Jpeg segment identifier. */
public static final byte SEGMENT_APP9 = (byte)0xE9;
/** APPA Jpeg segment identifier -- can hold Unicode comments. */
public static final byte SEGMENT_APPA = (byte)0xEA;
/** APPB Jpeg segment identifier. */
public static final byte SEGMENT_APPB = (byte)0xEB;
/** APPC Jpeg segment identifier. */
public static final byte SEGMENT_APPC = (byte)0xEC;
/** APPD Jpeg segment identifier -- IPTC data in here. */
public static final byte SEGMENT_APPD = (byte)0xED;
/** APPE Jpeg segment identifier. */
public static final byte SEGMENT_APPE = (byte)0xEE;
/** APPF Jpeg segment identifier. */
public static final byte SEGMENT_APPF = (byte)0xEF;
/** Start Of Image segment identifier. */
public static final byte SEGMENT_SOI = (byte)0xD8;
/** Define Quantization Table segment identifier. */
public static final byte SEGMENT_DQT = (byte)0xDB;
/** Define Huffman Table segment identifier. */
public static final byte SEGMENT_DHT = (byte)0xC4;
/** Start-of-Frame Zero segment identifier. */
public static final byte SEGMENT_SOF0 = (byte)0xC0;
/** Jpeg comment segment identifier. */
public static final byte SEGMENT_COM = (byte)0xFE;
/**
* Creates a JpegSegmentReader for a specific file.
* @param file the Jpeg file to read segments from
*/
public JpegSegmentReader(File file) throws JpegProcessingException
{
_file = file;
_data = null;
_stream = null;
readSegments();
}
/**
* Creates a JpegSegmentReader for a byte array.
* @param fileContents the byte array containing Jpeg data
*/
public JpegSegmentReader(byte[] fileContents) throws JpegProcessingException
{
_file = null;
_data = fileContents;
_stream = null;
readSegments();
}
public JpegSegmentReader(InputStream in) throws JpegProcessingException
{
_stream = in;
_file = null;
_data = null;
readSegments();
}
public JpegSegmentReader(JpegSegmentData segmentData)
{
_file = null;
_data = null;
_stream = null;
_segmentData = segmentData;
}
/**
* Reads the first instance of a given Jpeg segment, returning the contents as
* a byte array.
* @param segmentMarker the byte identifier for the desired segment
* @return the byte array if found, else null
* @throws JpegProcessingException for any problems processing the Jpeg data,
* including inner IOExceptions
*/
public byte[] readSegment(byte segmentMarker) throws JpegProcessingException
{
return readSegment(segmentMarker, 0);
}
/**
* Reads the first instance of a given Jpeg segment, returning the contents as
* a byte array.
* @param segmentMarker the byte identifier for the desired segment
* @param occurrence the occurrence of the specified segment within the jpeg file
* @return the byte array if found, else null
*/
public byte[] readSegment(byte segmentMarker, int occurrence)
{
return _segmentData.getSegment(segmentMarker, occurrence);
}
public final int getSegmentCount(byte segmentMarker)
{
return _segmentData.getSegmentCount(segmentMarker);
}
public final JpegSegmentData getSegmentData()
{
return _segmentData;
}
private void readSegments() throws JpegProcessingException
{
_segmentData = new JpegSegmentData();
BufferedInputStream inStream = getJpegInputStream();
try {
int offset = 0;
// first two bytes should be jpeg magic number
if (!isValidJpegHeaderBytes(inStream)) {
throw new JpegProcessingException("not a jpeg file");
}
offset += 2;
do {
// next byte is 0xFF
byte segmentIdentifier = (byte)(inStream.read() & 0xFF);
if ((segmentIdentifier & 0xFF) != 0xFF) {
throw new JpegProcessingException("expected jpeg segment start identifier 0xFF at offset " + offset + ", not 0x" + Integer.toHexString(segmentIdentifier & 0xFF));
}
offset++;
// next byte is
byte thisSegmentMarker = (byte)(inStream.read() & 0xFF);
offset++;
// next 2-bytes are : [high-byte] [low-byte]
byte[] segmentLengthBytes = new byte[2];
inStream.read(segmentLengthBytes, 0, 2);
offset += 2;
int segmentLength = ((segmentLengthBytes[0] << 8) & 0xFF00) | (segmentLengthBytes[1] & 0xFF);
// segment length includes size bytes, so subtract two
segmentLength -= 2;
if (segmentLength > inStream.available())
throw new JpegProcessingException("segment size would extend beyond file stream length");
else if (segmentLength < 0)
throw new JpegProcessingException("segment size would be less than zero");
byte[] segmentBytes = new byte[segmentLength];
inStream.read(segmentBytes, 0, segmentLength);
offset += segmentLength;
if ((thisSegmentMarker & 0xFF) == (SEGMENT_SOS & 0xFF)) {
// The 'Start-Of-Scan' segment's length doesn't include the image data, instead would
// have to search for the two bytes: 0xFF 0xD9 (EOI).
// It comes last so simply return at this point
return;
} else if ((thisSegmentMarker & 0xFF) == (MARKER_EOI & 0xFF)) {
// the 'End-Of-Image' segment -- this should never be found in this fashion
return;
} else {
_segmentData.addSegment(thisSegmentMarker, segmentBytes);
}
// didn't find the one we're looking for, loop through to the next segment
} while (true);
} catch (IOException ioe) {
//throw new JpegProcessingException("IOException processing Jpeg file", ioe);
throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
} finally {
try {
if (inStream != null) {
inStream.close();
}
} catch (IOException ioe) {
//throw new JpegProcessingException("IOException processing Jpeg file", ioe);
throw new JpegProcessingException("IOException processing Jpeg file: " + ioe.getMessage(), ioe);
}
}
}
/**
* Private helper method to create a BufferedInputStream of Jpeg data from whichever
* data source was specified upon construction of this instance.
* @return a a BufferedInputStream of Jpeg data
* @throws JpegProcessingException for any problems obtaining the stream
*/
private BufferedInputStream getJpegInputStream() throws JpegProcessingException
{
if (_stream!=null) {
if (_stream instanceof BufferedInputStream) {
return (BufferedInputStream) _stream;
} else {
return new BufferedInputStream(_stream);
}
}
InputStream inputStream;
if (_data == null) {
try {
inputStream = new FileInputStream(_file);
} catch (FileNotFoundException e) {
throw new JpegProcessingException("Jpeg file does not exist", e);
}
} else {
inputStream = new ByteArrayInputStream(_data);
}
return new BufferedInputStream(inputStream);
}
/**
* Helper method that validates the Jpeg file's magic number.
* @param fileStream the InputStream to read bytes from, which must be positioned
* at its start (i.e. no bytes read yet)
* @return true if the magic number is Jpeg (0xFFD8)
* @throws IOException for any problem in reading the file
*/
private boolean isValidJpegHeaderBytes(InputStream fileStream) throws IOException
{
byte[] header = new byte[2];
fileStream.read(header, 0, 2);
return (header[0] & 0xFF) == 0xFF && (header[1] & 0xFF) == 0xD8;
}
} libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/test/ 0000755 0000000 0000000 00000000000 10716136374 023247 5 ustar root root libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/test/JpegMetadataReaderTest.java 0000644 0000000 0000000 00000004340 10716136374 030424 0 ustar root root /*
* Test class written by Drew Noakes.
*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created by dnoakes on 12-Nov-2002 18:52:05 using IntelliJ IDEA.
*/
package com.drew.imaging.jpeg.test;
import com.drew.imaging.jpeg.JpegMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifDirectory;
import junit.framework.TestCase;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
*
*/
public class JpegMetadataReaderTest extends TestCase
{
public JpegMetadataReaderTest(String s)
{
super(s);
}
public void testExtractMetadata() throws Exception
{
File withExif = new File("src/com/drew/metadata/exif/test/withExif.jpg");
Metadata metadata = JpegMetadataReader.readMetadata(withExif);
assertTrue(metadata.containsDirectory(ExifDirectory.class));
Directory directory = metadata.getDirectory(ExifDirectory.class);
assertEquals("80", directory.getString(ExifDirectory.TAG_ISO_EQUIVALENT));
}
public void testExtractMetadataUsingInputStream() throws Exception
{
File withExif = new File("src/com/drew/metadata/exif/test/withExif.jpg");
InputStream in = new BufferedInputStream(new FileInputStream((withExif)));
Metadata metadata = JpegMetadataReader.readMetadata(in);
assertTrue(metadata.containsDirectory(ExifDirectory.class));
Directory directory = metadata.getDirectory(ExifDirectory.class);
assertEquals("80", directory.getString(ExifDirectory.TAG_ISO_EQUIVALENT));
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/test/JpegSegmentDataTest.java 0000644 0000000 0000000 00000013507 10716136374 027762 0 ustar root root /*
* Test class written by Drew Noakes.
*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*/
package com.drew.imaging.jpeg.test;
import com.drew.imaging.jpeg.JpegSegmentData;
import com.drew.lang.test.TestHelper;
import junit.framework.TestCase;
import java.io.File;
/**
*
*/
public class JpegSegmentDataTest extends TestCase
{
public JpegSegmentDataTest(String name)
{
super(name);
}
public void testAddAndGetSegment() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes = new byte[] { 1,2,3 };
segmentData.addSegment(segmentMarker, segmentBytes);
assertEquals(1, segmentData.getSegmentCount(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes, segmentData.getSegment(segmentMarker));
}
public void testContainsSegment() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes = new byte[] { 1,2,3 };
assertTrue(!segmentData.containsSegment(segmentMarker));
segmentData.addSegment(segmentMarker, segmentBytes);
assertTrue(segmentData.containsSegment(segmentMarker));
}
public void testAddingMultipleSegments() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker1 = (byte)12;
byte segmentMarker2 = (byte)21;
byte[] segmentBytes1 = new byte[] { 1,2,3 };
byte[] segmentBytes2 = new byte[] { 3,2,1 };
segmentData.addSegment(segmentMarker1, segmentBytes1);
segmentData.addSegment(segmentMarker2, segmentBytes2);
assertEquals(1, segmentData.getSegmentCount(segmentMarker1));
assertEquals(1, segmentData.getSegmentCount(segmentMarker2));
TestHelper.assertEqualArrays(segmentBytes1, segmentData.getSegment(segmentMarker1));
TestHelper.assertEqualArrays(segmentBytes2, segmentData.getSegment(segmentMarker2));
}
public void testSegmentWithMultipleOccurrences() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes1 = new byte[] { 1,2,3 };
byte[] segmentBytes2 = new byte[] { 3,2,1 };
segmentData.addSegment(segmentMarker, segmentBytes1);
segmentData.addSegment(segmentMarker, segmentBytes2);
assertEquals(2, segmentData.getSegmentCount(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes1, segmentData.getSegment(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes1, segmentData.getSegment(segmentMarker, 0));
TestHelper.assertEqualArrays(segmentBytes2, segmentData.getSegment(segmentMarker, 1));
}
public void testRemoveSegmentOccurrence() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes1 = new byte[] { 1,2,3 };
byte[] segmentBytes2 = new byte[] { 3,2,1 };
segmentData.addSegment(segmentMarker, segmentBytes1);
segmentData.addSegment(segmentMarker, segmentBytes2);
assertEquals(2, segmentData.getSegmentCount(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes1, segmentData.getSegment(segmentMarker, 0));
segmentData.removeSegmentOccurrence(segmentMarker, 0);
TestHelper.assertEqualArrays(segmentBytes2, segmentData.getSegment(segmentMarker, 0));
}
public void testRemoveSegment() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes1 = new byte[] { 1,2,3 };
byte[] segmentBytes2 = new byte[] { 3,2,1 };
segmentData.addSegment(segmentMarker, segmentBytes1);
segmentData.addSegment(segmentMarker, segmentBytes2);
assertEquals(2, segmentData.getSegmentCount(segmentMarker));
assertTrue(segmentData.containsSegment(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes1, segmentData.getSegment(segmentMarker, 0));
segmentData.removeSegment(segmentMarker);
assertTrue(!segmentData.containsSegment(segmentMarker));
assertEquals(0, segmentData.getSegmentCount(segmentMarker));
}
public void testToAndFromFile() throws Exception
{
JpegSegmentData segmentData = new JpegSegmentData();
byte segmentMarker = (byte)12;
byte[] segmentBytes = new byte[] { 1,2,3 };
segmentData.addSegment(segmentMarker, segmentBytes);
assertTrue(segmentData.containsSegment(segmentMarker));
File tempFile = File.createTempFile("JpegSegmentDataTest", "tmp");
JpegSegmentData.ToFile(tempFile, segmentData);
assertTrue(tempFile.exists());
assertTrue(tempFile.length() > 0);
segmentData = JpegSegmentData.FromFile(tempFile);
tempFile.delete();
assertTrue(!tempFile.exists());
assertNotNull(segmentData);
assertTrue(segmentData.containsSegment(segmentMarker));
TestHelper.assertEqualArrays(segmentBytes, segmentData.getSegment(segmentMarker));
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/test/JpegSegmentReaderTest.java 0000644 0000000 0000000 00000014712 10716136374 030312 0 ustar root root /*
* JpegSegmentReaderTest.java
*
* Test class written by Drew Noakes.
*
* This 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. 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.
*
* If you make use of this code, I'd appreciate hearing about it.
* drew@drewnoakes.com
* Latest version of this software kept at
* http://drewnoakes.com/
*
* Created by dnoakes on 04-Nov-2002 00:54:00 using IntelliJ IDEA
*/
package com.drew.imaging.jpeg.test;
import com.drew.imaging.jpeg.JpegProcessingException;
import com.drew.imaging.jpeg.JpegSegmentReader;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.iptc.IptcReader;
import junit.framework.TestCase;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* Contains JUnit tests for the JpegSegmentReader class.
*/
public class JpegSegmentReaderTest extends TestCase
{
public JpegSegmentReaderTest(String s)
{
super(s);
}
public void testIsJpegWithJpegFile() throws Exception
{
File jpeg = new File("src/com/drew/metadata/exif/test/withExif.jpg");
try {
new JpegSegmentReader(jpeg);
} catch (JpegProcessingException e) {
fail("Error creating JpegSegmentReader");
}
}
public void testIsJpegWithNonJpegFile() throws Exception
{
File nonJpeg = new File("src/com/drew/metadata/test/AllTests.java");
try {
new JpegSegmentReader(nonJpeg);
fail("shouldn't be able to construct JpegSegmentReader with non-jpeg file");
} catch (JpegProcessingException e) {
// expect exception
}
}
public void testReadApp1Segment() throws Exception
{
File jpeg = new File("src/com/drew/metadata/exif/test/withExif.jpg");
JpegSegmentReader segmentReader = new JpegSegmentReader(jpeg);
byte[] exifData = segmentReader.readSegment(JpegSegmentReader.SEGMENT_APP1);
assertTrue("exif data too short", exifData.length > 4);
assertEquals("Exif", new String(exifData, 0, 4));
}
public void testReadDQTSegment() throws Exception
{
File jpeg = new File("src/com/drew/metadata/exif/test/withExif.jpg");
JpegSegmentReader segmentReader = new JpegSegmentReader(jpeg);
byte[] quantizationTableData = segmentReader.readSegment(JpegSegmentReader.SEGMENT_DQT);
assertTrue("shouldn't have zero length quantizationTableData", quantizationTableData.length > 0);
assertTrue("quantizationTableData shouldn't start with 'Exif'", !"Exif".equals(new String(quantizationTableData, 0, 4)));
}
public void testReadJpegByteArray() throws Exception
{
File jpeg = new File("src/com/drew/metadata/exif/test/withExif.jpg");
byte[] fileContents = new byte[(int)jpeg.length()];
new FileInputStream(jpeg).read(fileContents);
new JpegSegmentReader(fileContents).readSegment(JpegSegmentReader.SEGMENT_APP1);
}
public void testCreateWithInputStream() throws Exception
{
File jpeg = new File("src/com/drew/metadata/exif/test/withExif.jpg");
InputStream in = new FileInputStream(jpeg);
JpegSegmentReader reader = null;
try {
reader = new JpegSegmentReader(in);
} catch (JpegProcessingException e) {
fail("Error constructing JpegSegmentReader using InputStream");
}
// this will never happen, as fail() is guaranteed to throw an AssertionException
if (reader==null)
return;
byte[] exifData = reader.readSegment(JpegSegmentReader.SEGMENT_APP1);
assertEquals("Exif", new String(exifData, 0, 4));
}
public void testReadSecondSegmentInstanace() throws Exception
{
File jpeg = new File("src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg");
JpegSegmentReader reader = new JpegSegmentReader(jpeg);
byte[] exifData0 = reader.readSegment(JpegSegmentReader.SEGMENT_APP1, 0);
byte[] exifData1 = reader.readSegment(JpegSegmentReader.SEGMENT_APP1, 1);
assertEquals("Exif", new String(exifData0, 0, 4));
assertEquals("http", new String(exifData1, 0, 4));
}
public void testReadNonExistantSegmentInstance() throws Exception
{
File jpeg = new File("src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg");
JpegSegmentReader reader = new JpegSegmentReader(jpeg);
assertNull("third exif segment shouldn't exist", reader.readSegment(JpegSegmentReader.SEGMENT_APP1, 3));
}
public void testGetSegmentCount() throws Exception
{
File jpeg = new File("src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg");
JpegSegmentReader reader = new JpegSegmentReader(jpeg);
assertEquals(2, reader.getSegmentCount(JpegSegmentReader.SEGMENT_APP1));
assertEquals(1, reader.getSegmentCount(JpegSegmentReader.SEGMENT_APP2));
assertEquals(0, reader.getSegmentCount(JpegSegmentReader.SEGMENT_APP3));
}
public void testCreateWithFileAndReadMultipleSegments() throws Exception
{
File jpeg = new File("src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg");
JpegSegmentReader reader = new JpegSegmentReader(jpeg);
validateMultipleSegmentRead(reader);
}
public void testCreateWithInputStreamAndReadMultipleSegments() throws Exception
{
File jpeg = new File("src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg");
InputStream in = new FileInputStream(jpeg);
JpegSegmentReader reader = new JpegSegmentReader(in);
validateMultipleSegmentRead(reader);
}
private void validateMultipleSegmentRead(JpegSegmentReader reader) throws JpegProcessingException
{
byte[] iptcData = reader.readSegment(JpegSegmentReader.SEGMENT_APPD);
byte[] exifData = reader.readSegment(JpegSegmentReader.SEGMENT_APP1);
assertTrue("exif data too short", exifData.length > 4);
new ExifReader(exifData).extract();
new IptcReader(iptcData).extract();
assertEquals("Exif", new String(exifData, 0, 4));
}
}
libmetadata-extractor-java-2.3.1/src/com/drew/imaging/jpeg/test/withExifAndIptc.jpg 0000644 0000000 0000000 00000127076 10716136374 027020 0 ustar root root JFIF ,, dExif II*
( 1 2 ; + [ i x % Communications FUJIFILM FinePixS1Pro , , Adobe Photoshop 7.0 2002:07:19 13:28:10 Ian Britton ian Britton - FreeFoto.com " ' 0200
0100 ` @ @ d2002:07:13 15:58:28 2002:07:13 15:58:28 L ` d d
N b W z 6 2 d m d : WGS84 ( N H H JFIF H H Adobe_CM Adobe d
U " ?
3 !1AQa"q2B#$Rb34rC%Scs5&DTdE£t6UeuF'Vfv7GWgw 5 !1AQaq"2B#R3$brCScs4%&5DTdEU6teuFVfv'7GWgw ? ֓;~)'`PH)5vհ
k&M7mgf ma;SpyCίm>unZƵ]k}Է f7-1xgfPl=*QUuVWw]a֨W)_'SU/5K5[:$Y#Y5!O4*۲|9x::χ5f~ihԤsEu<`cvl|~ov0$ysIqߚ huP\Zmi-pn?}ٳP}ݖ쥁e|6z 9_*^/GEm`A659iȣ=V}ZP,/~eO?o[|8N̹D@D7ڞ#h% -79dMʶ100iwu,4S0l-ߺ6.G'0\ro`sA?;+v SbߍGCV욻Nt45KF5;V_QfTx
0Xi߲Y7Z}FHO{-nq\<PX6`5?i;wgk:FM &LSh.[ߴsٳn*~]w1+c1lߢo WF`JaJ)eYqՕcˁs9ߥc,Ŧcok$Z d/?Foڻՠz&=xQvVo\ ߟgWdK_`-3XwmwPIJQlP .>m}OFS_p"~{kۃw5ETHWG}B=2{Fݣvs 6ޟU4utAS`|J굎wՂsDD7:SJe LѢ):&V;GsjC{Zo?EV ~Ӊ4
`vv
츳esk=Rksյ[s/[Sksa=?ؕy^=ci7׳Kll7vֳ ON|_]Zmַ55]
+(z;^Ż6nkk'ꈪa>nOf;j^wA7{ֶemd;tIhﴆ oآܪ3W4IovAޘdQ=s]Ei[_]4:7u-eLHGc~dAiX?k[ۑV5iH!{~km b'K`\ \omgFً[M-yƵ% ~b+GRe W