0,0 → 1,225 |
/* |
* 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 19:00:03 using IntelliJ IDEA. |
*/ |
package com.drew.metadata.iptc; |
|
import com.drew.imaging.jpeg.JpegProcessingException; |
import com.drew.imaging.jpeg.JpegSegmentReader; |
import com.drew.metadata.Directory; |
import com.drew.metadata.Metadata; |
import com.drew.metadata.MetadataException; |
import com.drew.metadata.MetadataReader; |
|
import java.io.File; |
import java.io.InputStream; |
import java.util.Date; |
|
/** |
* |
*/ |
public class IptcReader implements MetadataReader |
{ |
/* |
public static final int DIRECTORY_IPTC = 2; |
|
public static final int ENVELOPE_RECORD = 1; |
public static final int APPLICATION_RECORD_2 = 2; |
public static final int APPLICATION_RECORD_3 = 3; |
public static final int APPLICATION_RECORD_4 = 4; |
public static final int APPLICATION_RECORD_5 = 5; |
public static final int APPLICATION_RECORD_6 = 6; |
public static final int PRE_DATA_RECORD = 7; |
public static final int DATA_RECORD = 8; |
public static final int POST_DATA_RECORD = 9; |
*/ |
/** |
* The Iptc data segment. |
*/ |
private final byte[] _data; |
|
/** |
* Creates a new IptcReader for the specified Jpeg jpegFile. |
*/ |
public IptcReader(File jpegFile) throws JpegProcessingException |
{ |
this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_APPD)); |
} |
|
/** Creates an IptcReader for a JPEG stream. |
* |
* @param is JPEG stream. Stream will be closed. |
*/ |
public IptcReader(InputStream is) throws JpegProcessingException |
{ |
this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD)); |
} |
|
public IptcReader(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) |
{ |
if (_data == null) { |
return metadata; |
} |
|
Directory directory = metadata.getDirectory(IptcDirectory.class); |
|
// find start of data |
int offset = 0; |
try { |
while (offset < _data.length - 1 && get32Bits(offset) != 0x1c02) { |
offset++; |
} |
} catch (MetadataException e) { |
directory.addError("Couldn't find start of Iptc data (invalid segment)"); |
return metadata; |
} |
|
// for each tag |
while (offset < _data.length) { |
// identifies start of a tag |
if (_data[offset] != 0x1c) { |
break; |
} |
// we need at least five bytes left to read a tag |
if ((offset + 5) >= _data.length) { |
break; |
} |
|
offset++; |
|
int directoryType; |
int tagType; |
int tagByteCount; |
try { |
directoryType = _data[offset++]; |
tagType = _data[offset++]; |
tagByteCount = get32Bits(offset); |
} catch (MetadataException e) { |
directory.addError("Iptc data segment ended mid-way through tag descriptor"); |
return metadata; |
} |
offset += 2; |
if ((offset + tagByteCount) > _data.length) { |
directory.addError("data for tag extends beyond end of iptc segment"); |
break; |
} |
|
processTag(directory, directoryType, tagType, offset, tagByteCount); |
offset += tagByteCount; |
} |
|
return metadata; |
} |
|
/** |
* Returns an int calculated from two bytes of data at the specified offset (MSB, LSB). |
* @param offset position within the data buffer to read first byte |
* @return the 32 bit int value, between 0x0000 and 0xFFFF |
*/ |
private int get32Bits(int offset) throws MetadataException |
{ |
if (offset >= _data.length) { |
throw new MetadataException("Attempt to read bytes from outside Iptc data buffer"); |
} |
return ((_data[offset] & 255) << 8) | (_data[offset + 1] & 255); |
} |
|
/** |
* This method serves as marsheller of objects for dataset. It converts from IPTC |
* octets to relevant java object. |
*/ |
private void processTag(Directory directory, int directoryType, int tagType, int offset, int tagByteCount) |
{ |
int tagIdentifier = tagType | (directoryType << 8); |
|
switch (tagIdentifier) { |
case IptcDirectory.TAG_RECORD_VERSION: |
// short |
short shortValue = (short)((_data[offset] << 8) | _data[offset + 1]); |
directory.setInt(tagIdentifier, shortValue); |
return; |
case IptcDirectory.TAG_URGENCY: |
// byte |
directory.setInt(tagIdentifier, _data[offset]); |
return; |
case IptcDirectory.TAG_RELEASE_DATE: |
case IptcDirectory.TAG_DATE_CREATED: |
// Date object |
if (tagByteCount >= 8) { |
String dateStr = new String(_data, offset, tagByteCount); |
try { |
int year = Integer.parseInt(dateStr.substring(0, 4)); |
int month = Integer.parseInt(dateStr.substring(4, 6)) - 1; |
int day = Integer.parseInt(dateStr.substring(6, 8)); |
Date date = (new java.util.GregorianCalendar(year, month, day)).getTime(); |
directory.setDate(tagIdentifier, date); |
return; |
} catch (NumberFormatException e) { |
// fall through and we'll store whatever was there as a String |
} |
} |
case IptcDirectory.TAG_RELEASE_TIME: |
case IptcDirectory.TAG_TIME_CREATED: |
// time... |
default: |
// fall through |
} |
// If no special handling by now, treat it as a string |
String str; |
if (tagByteCount < 1) { |
str = ""; |
} else { |
str = new String(_data, offset, tagByteCount); |
} |
if (directory.containsTag(tagIdentifier)) { |
String[] oldStrings; |
String[] newStrings; |
try { |
oldStrings = directory.getStringArray(tagIdentifier); |
} catch (MetadataException e) { |
oldStrings = null; |
} |
if (oldStrings == null) { |
newStrings = new String[1]; |
} else { |
newStrings = new String[oldStrings.length + 1]; |
for (int i = 0; i < oldStrings.length; i++) { |
newStrings[i] = oldStrings[i]; |
} |
} |
newStrings[newStrings.length - 1] = str; |
directory.setStringArray(tagIdentifier, newStrings); |
} else { |
directory.setString(tagIdentifier, str); |
} |
} |
} |