source: josm/trunk/src/com/drew/metadata/iptc/IptcReader.java@ 4231

Last change on this file since 4231 was 4231, checked in by stoecker, 13 years ago

add signpost and metadata extractor code to repository directly

File size: 7.8 KB
Line 
1/*
2 * This is public domain software - that is, you can do whatever you want
3 * with it, and include it software that is licensed under the GNU or the
4 * BSD license, or whatever other licence you choose, including proprietary
5 * closed source licenses. I do ask that you leave this header in tact.
6 *
7 * If you make modifications to this code that you think would benefit the
8 * wider community, please send me a copy and I'll post it on my site.
9 *
10 * If you make use of this code, I'd appreciate hearing about it.
11 * drew@drewnoakes.com
12 * Latest version of this software kept at
13 * http://drewnoakes.com/
14 *
15 * Created by dnoakes on 12-Nov-2002 19:00:03 using IntelliJ IDEA.
16 */
17package com.drew.metadata.iptc;
18
19import com.drew.imaging.jpeg.JpegProcessingException;
20import com.drew.imaging.jpeg.JpegSegmentReader;
21import com.drew.metadata.Directory;
22import com.drew.metadata.Metadata;
23import com.drew.metadata.MetadataException;
24import com.drew.metadata.MetadataReader;
25
26import java.io.File;
27import java.io.InputStream;
28import java.util.Date;
29
30/**
31 *
32 */
33public class IptcReader implements MetadataReader
34{
35/*
36 public static final int DIRECTORY_IPTC = 2;
37
38 public static final int ENVELOPE_RECORD = 1;
39 public static final int APPLICATION_RECORD_2 = 2;
40 public static final int APPLICATION_RECORD_3 = 3;
41 public static final int APPLICATION_RECORD_4 = 4;
42 public static final int APPLICATION_RECORD_5 = 5;
43 public static final int APPLICATION_RECORD_6 = 6;
44 public static final int PRE_DATA_RECORD = 7;
45 public static final int DATA_RECORD = 8;
46 public static final int POST_DATA_RECORD = 9;
47*/
48 /**
49 * The Iptc data segment.
50 */
51 private final byte[] _data;
52
53 /**
54 * Creates a new IptcReader for the specified Jpeg jpegFile.
55 */
56 public IptcReader(File jpegFile) throws JpegProcessingException
57 {
58 this(new JpegSegmentReader(jpegFile).readSegment(JpegSegmentReader.SEGMENT_APPD));
59 }
60
61 /** Creates an IptcReader for a JPEG stream.
62 *
63 * @param is JPEG stream. Stream will be closed.
64 */
65 public IptcReader(InputStream is) throws JpegProcessingException
66 {
67 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APPD));
68 }
69
70 public IptcReader(byte[] data)
71 {
72 _data = data;
73 }
74
75 /**
76 * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>.
77 */
78 public Metadata extract()
79 {
80 return extract(new Metadata());
81 }
82
83 /**
84 * Performs the Exif data extraction, adding found values to the specified
85 * instance of <code>Metadata</code>.
86 */
87 public Metadata extract(Metadata metadata)
88 {
89 if (_data == null) {
90 return metadata;
91 }
92
93 Directory directory = metadata.getDirectory(IptcDirectory.class);
94
95 // find start of data
96 int offset = 0;
97 try {
98 while (offset < _data.length - 1 && get32Bits(offset) != 0x1c02) {
99 offset++;
100 }
101 } catch (MetadataException e) {
102 directory.addError("Couldn't find start of Iptc data (invalid segment)");
103 return metadata;
104 }
105
106 // for each tag
107 while (offset < _data.length) {
108 // identifies start of a tag
109 if (_data[offset] != 0x1c) {
110 break;
111 }
112 // we need at least five bytes left to read a tag
113 if ((offset + 5) >= _data.length) {
114 break;
115 }
116
117 offset++;
118
119 int directoryType;
120 int tagType;
121 int tagByteCount;
122 try {
123 directoryType = _data[offset++];
124 tagType = _data[offset++];
125 tagByteCount = get32Bits(offset);
126 } catch (MetadataException e) {
127 directory.addError("Iptc data segment ended mid-way through tag descriptor");
128 return metadata;
129 }
130 offset += 2;
131 if ((offset + tagByteCount) > _data.length) {
132 directory.addError("data for tag extends beyond end of iptc segment");
133 break;
134 }
135
136 processTag(directory, directoryType, tagType, offset, tagByteCount);
137 offset += tagByteCount;
138 }
139
140 return metadata;
141 }
142
143 /**
144 * Returns an int calculated from two bytes of data at the specified offset (MSB, LSB).
145 * @param offset position within the data buffer to read first byte
146 * @return the 32 bit int value, between 0x0000 and 0xFFFF
147 */
148 private int get32Bits(int offset) throws MetadataException
149 {
150 if (offset >= _data.length) {
151 throw new MetadataException("Attempt to read bytes from outside Iptc data buffer");
152 }
153 return ((_data[offset] & 255) << 8) | (_data[offset + 1] & 255);
154 }
155
156 /**
157 * This method serves as marsheller of objects for dataset. It converts from IPTC
158 * octets to relevant java object.
159 */
160 private void processTag(Directory directory, int directoryType, int tagType, int offset, int tagByteCount)
161 {
162 int tagIdentifier = tagType | (directoryType << 8);
163
164 switch (tagIdentifier) {
165 case IptcDirectory.TAG_RECORD_VERSION:
166 // short
167 short shortValue = (short)((_data[offset] << 8) | _data[offset + 1]);
168 directory.setInt(tagIdentifier, shortValue);
169 return;
170 case IptcDirectory.TAG_URGENCY:
171 // byte
172 directory.setInt(tagIdentifier, _data[offset]);
173 return;
174 case IptcDirectory.TAG_RELEASE_DATE:
175 case IptcDirectory.TAG_DATE_CREATED:
176 // Date object
177 if (tagByteCount >= 8) {
178 String dateStr = new String(_data, offset, tagByteCount);
179 try {
180 int year = Integer.parseInt(dateStr.substring(0, 4));
181 int month = Integer.parseInt(dateStr.substring(4, 6)) - 1;
182 int day = Integer.parseInt(dateStr.substring(6, 8));
183 Date date = (new java.util.GregorianCalendar(year, month, day)).getTime();
184 directory.setDate(tagIdentifier, date);
185 return;
186 } catch (NumberFormatException e) {
187 // fall through and we'll store whatever was there as a String
188 }
189 }
190 case IptcDirectory.TAG_RELEASE_TIME:
191 case IptcDirectory.TAG_TIME_CREATED:
192 // time...
193 default:
194 // fall through
195 }
196 // If no special handling by now, treat it as a string
197 String str;
198 if (tagByteCount < 1) {
199 str = "";
200 } else {
201 str = new String(_data, offset, tagByteCount);
202 }
203 if (directory.containsTag(tagIdentifier)) {
204 String[] oldStrings;
205 String[] newStrings;
206 try {
207 oldStrings = directory.getStringArray(tagIdentifier);
208 } catch (MetadataException e) {
209 oldStrings = null;
210 }
211 if (oldStrings == null) {
212 newStrings = new String[1];
213 } else {
214 newStrings = new String[oldStrings.length + 1];
215 for (int i = 0; i < oldStrings.length; i++) {
216 newStrings[i] = oldStrings[i];
217 }
218 }
219 newStrings[newStrings.length - 1] = str;
220 directory.setStringArray(tagIdentifier, newStrings);
221 } else {
222 directory.setString(tagIdentifier, str);
223 }
224 }
225}
Note: See TracBrowser for help on using the repository browser.