source: josm/trunk/src/com/drew/metadata/exif/ExifReader.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: 30.2 KB
Line 
1/*
2 * EXIFExtractor.java
3 *
4 * This class based upon code from Jhead, a C program for extracting and
5 * manipulating the Exif data within files written by Matthias Wandel.
6 * http://www.sentex.net/~mwandel/jhead/
7 *
8 * Jhead is public domain software - that is, you can do whatever you want
9 * with it, and include it software that is licensed under the GNU or the
10 * BSD license, or whatever other licence you choose, including proprietary
11 * closed source licenses. Similarly, I release this Java version under the
12 * same license, though I do ask that you leave this header in tact.
13 *
14 * If you make modifications to this code that you think would benefit the
15 * wider community, please send me a copy and I'll post it on my site. Unlike
16 * Jhead, this code (as it stands) only supports reading of Exif data - no
17 * manipulation, and no thumbnail stuff.
18 *
19 * If you make use of this code, I'd appreciate hearing about it.
20 * drew.noakes@drewnoakes.com
21 * Latest version of this software kept at
22 * http://drewnoakes.com/
23 *
24 * Created on 28 April 2002, 23:54
25 * Modified 04 Aug 2002
26 * - Renamed constants to be inline with changes to ExifTagValues interface
27 * - Substituted usage of JDK 1.4 features (java.nio package)
28 * Modified 29 Oct 2002 (v1.2)
29 * - Proper traversing of Exif file structure and complete refactor & tidy of
30 * the codebase (a few unnoticed bugs removed)
31 * - Reads makernote data for 6 families of camera (5 makes)
32 * - Tags now stored in directories... use the IFD_* constants to refer to the
33 * image file directory you require (Exif, Interop, GPS and Makernote*) --
34 * this avoids collisions where two tags share the same code
35 * - Takes componentCount of unknown tags into account
36 * - Now understands GPS tags (thanks to Colin Briton for his help with this)
37 * - Some other bug fixes, pointed out by users around the world. Thanks!
38 * Modified 27 Nov 2002 (v2.0)
39 * - Renamed to ExifReader
40 * - Moved to new package com.drew.metadata.exif
41 * Modified since, however changes have not been logged. See release notes for
42 * library-wide modifications.
43 */
44package com.drew.metadata.exif;
45
46import com.drew.imaging.jpeg.JpegProcessingException;
47import com.drew.imaging.jpeg.JpegSegmentData;
48import com.drew.imaging.jpeg.JpegSegmentReader;
49import com.drew.lang.Rational;
50import com.drew.metadata.Directory;
51import com.drew.metadata.Metadata;
52import com.drew.metadata.MetadataReader;
53
54import java.io.File;
55import java.io.InputStream;
56import java.util.HashMap;
57
58/**
59 * Extracts Exif data from a JPEG header segment, providing information about the
60 * camera/scanner/capture device (if available). Information is encapsulated in
61 * an <code>Metadata</code> object.
62 * @author Drew Noakes http://drewnoakes.com
63 */
64public class ExifReader implements MetadataReader
65{
66 /**
67 * The JPEG segment as an array of bytes.
68 */
69 private final byte[] _data;
70
71 /**
72 * Represents the native byte ordering used in the JPEG segment. If true,
73 * then we're using Motorolla ordering (Big endian), else we're using Intel
74 * ordering (Little endian).
75 */
76 private boolean _isMotorollaByteOrder;
77
78 /**
79 * Bean instance to store information about the image and camera/scanner/capture
80 * device.
81 */
82 private Metadata _metadata;
83
84 /**
85 * The number of bytes used per format descriptor.
86 */
87 private static final int[] BYTES_PER_FORMAT = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8};
88
89 /**
90 * The number of formats known.
91 */
92 private static final int MAX_FORMAT_CODE = 12;
93
94 // Format types
95 // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.
96 // Is there a better way?
97 private static final int FMT_BYTE = 1;
98 private static final int FMT_STRING = 2;
99 private static final int FMT_USHORT = 3;
100 private static final int FMT_ULONG = 4;
101 private static final int FMT_URATIONAL = 5;
102 private static final int FMT_SBYTE = 6;
103 private static final int FMT_UNDEFINED = 7;
104 private static final int FMT_SSHORT = 8;
105 private static final int FMT_SLONG = 9;
106 private static final int FMT_SRATIONAL = 10;
107 private static final int FMT_SINGLE = 11;
108 private static final int FMT_DOUBLE = 12;
109
110 public static final int TAG_EXIF_OFFSET = 0x8769;
111 public static final int TAG_INTEROP_OFFSET = 0xA005;
112 public static final int TAG_GPS_INFO_OFFSET = 0x8825;
113 public static final int TAG_MAKER_NOTE = 0x927C;
114
115 public static final int TIFF_HEADER_START_OFFSET = 6;
116
117 /**
118 * Creates an ExifReader for a JpegSegmentData object.
119 * @param segmentData
120 */
121 public ExifReader(JpegSegmentData segmentData)
122 {
123 this(segmentData.getSegment(JpegSegmentReader.SEGMENT_APP1));
124 }
125
126 /**
127 * Creates an ExifReader for a Jpeg file.
128 * @param file
129 * @throws JpegProcessingException
130 */
131 public ExifReader(File file) throws JpegProcessingException
132 {
133 this(new JpegSegmentReader(file).readSegment(JpegSegmentReader.SEGMENT_APP1));
134 }
135
136 /**
137 * Creates an ExifReader for a Jpeg stream.
138 * @param is JPEG stream. Stream will be closed.
139 */
140 public ExifReader(InputStream is) throws JpegProcessingException
141 {
142 this(new JpegSegmentReader(is).readSegment(JpegSegmentReader.SEGMENT_APP1));
143 }
144
145 /**
146 * Creates an ExifReader for the given JPEG header segment.
147 */
148 public ExifReader(byte[] data)
149 {
150 _data = data;
151 }
152
153 /**
154 * Performs the Exif data extraction, returning a new instance of <code>Metadata</code>.
155 */
156 public Metadata extract()
157 {
158 return extract(new Metadata());
159 }
160
161 /**
162 * Performs the Exif data extraction, adding found values to the specified
163 * instance of <code>Metadata</code>.
164 */
165 public Metadata extract(Metadata metadata)
166 {
167 _metadata = metadata;
168 if (_data==null)
169 return _metadata;
170
171 // once we know there's some data, create the directory and start working on it
172 ExifDirectory directory = (ExifDirectory)_metadata.getDirectory(ExifDirectory.class);
173
174 // check for the header length
175 if (_data.length<=14) {
176 directory.addError("Exif data segment must contain at least 14 bytes");
177 return _metadata;
178 }
179
180 // check for the header preamble
181 if (!"Exif\0\0".equals(new String(_data, 0, 6))) {
182 directory.addError("Exif data segment doesn't begin with 'Exif'");
183 return _metadata;
184 }
185
186 // this should be either "MM" or "II"
187 String byteOrderIdentifier = new String(_data, 6, 2);
188 if (!setByteOrder(byteOrderIdentifier)) {
189 directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier);
190 return _metadata;
191 }
192
193 // Check the next two values for correctness.
194 if (get16Bits(8)!=0x2a) {
195 directory.addError("Invalid Exif start - should have 0x2A at offset 8 in Exif header");
196 return _metadata;
197 }
198
199 int firstDirectoryOffset = get32Bits(10) + TIFF_HEADER_START_OFFSET;
200
201 // David Ekholm sent an digital camera image that has this problem
202 if (firstDirectoryOffset>=_data.length - 1) {
203 directory.addError("First exif directory offset is beyond end of Exif data segment");
204 // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case
205 firstDirectoryOffset = 14;
206 }
207
208 HashMap processedDirectoryOffsets = new HashMap();
209
210 // 0th IFD (we merge with Exif IFD)
211 processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET);
212
213 // after the extraction process, if we have the correct tags, we may be able to store thumbnail information
214 storeThumbnailBytes(directory, TIFF_HEADER_START_OFFSET);
215
216 return _metadata;
217 }
218
219 private void storeThumbnailBytes(ExifDirectory exifDirectory, int tiffHeaderOffset)
220 {
221 if (!exifDirectory.containsTag(ExifDirectory.TAG_COMPRESSION))
222 return;
223
224 if (!exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_LENGTH) ||
225 !exifDirectory.containsTag(ExifDirectory.TAG_THUMBNAIL_OFFSET))
226 return;
227
228 try {
229 int offset = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_OFFSET);
230 int length = exifDirectory.getInt(ExifDirectory.TAG_THUMBNAIL_LENGTH);
231 byte[] result = new byte[length];
232 for (int i = 0; i<result.length; i++) {
233 result[i] = _data[tiffHeaderOffset + offset + i];
234 }
235 exifDirectory.setByteArray(ExifDirectory.TAG_THUMBNAIL_DATA, result);
236 } catch (Throwable e) {
237 exifDirectory.addError("Unable to extract thumbnail: " + e.getMessage());
238 }
239 }
240
241 private boolean setByteOrder(String byteOrderIdentifier)
242 {
243 if ("MM".equals(byteOrderIdentifier)) {
244 _isMotorollaByteOrder = true;
245 } else if ("II".equals(byteOrderIdentifier)) {
246 _isMotorollaByteOrder = false;
247 } else {
248 return false;
249 }
250 return true;
251 }
252
253 /**
254 * Process one of the nested Tiff IFD directories.
255 * 2 bytes: number of tags
256 * for each tag
257 * 2 bytes: tag type
258 * 2 bytes: format code
259 * 4 bytes: component count
260 */
261 private void processDirectory(Directory directory, HashMap processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset)
262 {
263 // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist
264 if (processedDirectoryOffsets.containsKey(new Integer(dirStartOffset)))
265 return;
266
267 // remember that we've visited this directory so that we don't visit it again later
268 processedDirectoryOffsets.put(new Integer(dirStartOffset), "processed");
269
270 if (dirStartOffset>=_data.length || dirStartOffset<0) {
271 directory.addError("Ignored directory marked to start outside data segement");
272 return;
273 }
274
275 if (!isDirectoryLengthValid(dirStartOffset, tiffHeaderOffset)) {
276 directory.addError("Illegally sized directory");
277 return;
278 }
279
280 // First two bytes in the IFD are the number of tags in this directory
281 int dirTagCount = get16Bits(dirStartOffset);
282
283 // Handle each tag in this directory
284 for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++)
285 {
286 final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber);
287
288 // 2 bytes for the tag type
289 final int tagType = get16Bits(tagOffset);
290
291 // 2 bytes for the format code
292 final int formatCode = get16Bits(tagOffset + 2);
293 if (formatCode<1 || formatCode>MAX_FORMAT_CODE) {
294 directory.addError("Invalid format code: " + formatCode);
295 continue;
296 }
297
298 // 4 bytes dictate the number of components in this tag's data
299 final int componentCount = get32Bits(tagOffset + 4);
300 if (componentCount<0) {
301 directory.addError("Negative component count in EXIF");
302 continue;
303 }
304 // each component may have more than one byte... calculate the total number of bytes
305 final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
306 final int tagValueOffset = calculateTagValueOffset(byteCount, tagOffset, tiffHeaderOffset);
307 if (tagValueOffset<0 || tagValueOffset > _data.length) {
308 directory.addError("Illegal pointer offset value in EXIF");
309 continue;
310 }
311
312 // Check that this tag isn't going to allocate outside the bounds of the data array.
313 // This addresses an uncommon OutOfMemoryError.
314 if (byteCount < 0 || tagValueOffset + byteCount > _data.length)
315 {
316 directory.addError("Illegal number of bytes: " + byteCount);
317 continue;
318 }
319
320 // Calculate the value as an offset for cases where the tag represents directory
321 final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);
322
323 switch (tagType) {
324 case TAG_EXIF_OFFSET:
325 processDirectory(_metadata.getDirectory(ExifDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
326 continue;
327 case TAG_INTEROP_OFFSET:
328 processDirectory(_metadata.getDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
329 continue;
330 case TAG_GPS_INFO_OFFSET:
331 processDirectory(_metadata.getDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
332 continue;
333 case TAG_MAKER_NOTE:
334 processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset);
335 continue;
336 default:
337 processTag(directory, tagType, tagValueOffset, componentCount, formatCode);
338 break;
339 }
340 }
341
342 // at the end of each IFD is an optional link to the next IFD
343 final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount);
344 int nextDirectoryOffset = get32Bits(finalTagOffset);
345 if (nextDirectoryOffset!=0) {
346 nextDirectoryOffset += tiffHeaderOffset;
347 if (nextDirectoryOffset>=_data.length) {
348 // Last 4 bytes of IFD reference another IFD with an address that is out of bounds
349 // Note this could have been caused by jhead 1.3 cropping too much
350 return;
351 } else if (nextDirectoryOffset < dirStartOffset) {
352 // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory
353 return;
354 }
355 // the next directory is of same type as this one
356 processDirectory(directory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset);
357 }
358 }
359
360 private void processMakerNote(int subdirOffset, HashMap processedDirectoryOffsets, int tiffHeaderOffset)
361 {
362 // Determine the camera model and makernote format
363 Directory exifDirectory = _metadata.getDirectory(ExifDirectory.class);
364
365 if (exifDirectory==null)
366 return;
367
368 String cameraModel = exifDirectory.getString(ExifDirectory.TAG_MAKE);
369 final String firstTwoChars = new String(_data, subdirOffset, 2);
370 final String firstThreeChars = new String(_data, subdirOffset, 3);
371 final String firstFourChars = new String(_data, subdirOffset, 4);
372 final String firstFiveChars = new String(_data, subdirOffset, 5);
373 final String firstSixChars = new String(_data, subdirOffset, 6);
374 final String firstSevenChars = new String(_data, subdirOffset, 7);
375 final String firstEightChars = new String(_data, subdirOffset, 8);
376 if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars))
377 {
378 // Olympus Makernote
379 // Epson and Agfa use Olypus maker note standard, see:
380 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/
381 processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
382 }
383 else if (cameraModel!=null && cameraModel.trim().toUpperCase().startsWith("NIKON"))
384 {
385 if ("Nikon".equals(firstFiveChars))
386 {
387 /* There are two scenarios here:
388 * Type 1: **
389 * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon...........
390 * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................
391 * Type 3: **
392 * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*...
393 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200
394 */
395 if (_data[subdirOffset+6]==1)
396 processDirectory(_metadata.getDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset);
397 else if (_data[subdirOffset+6]==2)
398 processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10);
399 else
400 exifDirectory.addError("Unsupported makernote data ignored.");
401 }
402 else
403 {
404 // The IFD begins with the first MakerNote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models.
405 processDirectory(_metadata.getDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
406 }
407 }
408 else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars))
409 {
410 processDirectory(_metadata.getDirectory(SonyMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
411 }
412 else if ("KDK".equals(firstThreeChars))
413 {
414 processDirectory(_metadata.getDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset);
415 }
416 else if ("Canon".equalsIgnoreCase(cameraModel))
417 {
418 processDirectory(_metadata.getDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
419 }
420 else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("CASIO"))
421 {
422 if ("QVC\u0000\u0000\u0000".equals(firstSixChars))
423 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset);
424 else
425 processDirectory(_metadata.getDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
426 }
427 else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel))
428 {
429 // TODO make this field a passed parameter, to avoid threading issues
430 boolean byteOrderBefore = _isMotorollaByteOrder;
431 // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering
432 _isMotorollaByteOrder = false;
433 // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote
434 // IFD, though the offset is relative to the start of the makernote, not the TIFF
435 // header (like everywhere else)
436 int ifdStart = subdirOffset + get32Bits(subdirOffset + 8);
437 processDirectory(_metadata.getDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset);
438 _isMotorollaByteOrder = byteOrderBefore;
439 }
440 else if (cameraModel!=null && cameraModel.toUpperCase().startsWith("MINOLTA"))
441 {
442 // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote
443 // area that commences immediately.
444 processDirectory(_metadata.getDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset);
445 }
446 else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars))
447 {
448 // This Konica data is not understood. Header identified in accordance with information at this site:
449 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html
450 // TODO determine how to process the information described at the above website
451 exifDirectory.addError("Unsupported Konica/Minolta data ignored.");
452 }
453 else if ("KYOCERA".equals(firstSevenChars))
454 {
455 // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html
456 processDirectory(_metadata.getDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset);
457 }
458 else if ("Panasonic\u0000\u0000\u0000".equals(new String(_data, subdirOffset, 12)))
459 {
460 // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD
461 // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment
462 // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html
463 processDirectory(_metadata.getDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset);
464 }
465 else if ("AOC\u0000".equals(firstFourChars))
466 {
467 // NON-Standard TIFF IFD Data using Casio Type 2 Tags
468 // IFD has no Next-IFD pointer at end of IFD, and
469 // Offsets are relative to the start of the current IFD tag, not the TIFF header
470 // Observed for:
471 // - Pentax ist D
472 processDirectory(_metadata.getDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset);
473 }
474 else if (cameraModel!=null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI")))
475 {
476 // NON-Standard TIFF IFD Data using Pentax Tags
477 // IFD has no Next-IFD pointer at end of IFD, and
478 // Offsets are relative to the start of the current IFD tag, not the TIFF header
479 // Observed for:
480 // - PENTAX Optio 330
481 // - PENTAX Optio 430
482 processDirectory(_metadata.getDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset);
483 }
484 else
485 {
486 // TODO how to store makernote data when it's not from a supported camera model?
487 // this is difficult as the starting offset is not known. we could look for it...
488 exifDirectory.addError("Unsupported makernote data ignored.");
489 }
490 }
491
492 private boolean isDirectoryLengthValid(int dirStartOffset, int tiffHeaderOffset)
493 {
494 int dirTagCount = get16Bits(dirStartOffset);
495 int dirLength = (2 + (12 * dirTagCount) + 4);
496 if (dirLength + dirStartOffset + tiffHeaderOffset>=_data.length) {
497 // Note: Files that had thumbnails trimmed with jhead 1.3 or earlier might trigger this
498 return false;
499 }
500 return true;
501 }
502
503 private void processTag(Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode)
504 {
505 // Directory simply stores raw values
506 // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions
507 switch (formatCode)
508 {
509 case FMT_UNDEFINED:
510 // this includes exif user comments
511 final byte[] tagBytes = new byte[componentCount];
512 final int byteCount = componentCount * BYTES_PER_FORMAT[formatCode];
513 for (int i=0; i<byteCount; i++)
514 tagBytes[i] = _data[tagValueOffset + i];
515 directory.setByteArray(tagType, tagBytes);
516 break;
517 case FMT_STRING:
518 directory.setString(tagType, readString(tagValueOffset, componentCount));
519 break;
520 case FMT_SRATIONAL:
521 case FMT_URATIONAL:
522 if (componentCount==1) {
523 Rational rational = new Rational(get32Bits(tagValueOffset), get32Bits(tagValueOffset + 4));
524 directory.setRational(tagType, rational);
525 } else {
526 Rational[] rationals = new Rational[componentCount];
527 for (int i = 0; i<componentCount; i++)
528 rationals[i] = new Rational(get32Bits(tagValueOffset + (8 * i)), get32Bits(tagValueOffset + 4 + (8 * i)));
529 directory.setRationalArray(tagType, rationals);
530 }
531 break;
532 case FMT_SBYTE:
533 case FMT_BYTE:
534 if (componentCount==1) {
535 // this may need to be a byte, but I think casting to int is fine
536 int b = _data[tagValueOffset];
537 directory.setInt(tagType, b);
538 } else {
539 int[] bytes = new int[componentCount];
540 for (int i = 0; i<componentCount; i++)
541 bytes[i] = _data[tagValueOffset + i];
542 directory.setIntArray(tagType, bytes);
543 }
544 break;
545 case FMT_SINGLE:
546 case FMT_DOUBLE:
547 if (componentCount==1) {
548 int i = _data[tagValueOffset];
549 directory.setInt(tagType, i);
550 } else {
551 int[] ints = new int[componentCount];
552 for (int i = 0; i<componentCount; i++)
553 ints[i] = _data[tagValueOffset + i];
554 directory.setIntArray(tagType, ints);
555 }
556 break;
557 case FMT_USHORT:
558 case FMT_SSHORT:
559 if (componentCount==1) {
560 int i = get16Bits(tagValueOffset);
561 directory.setInt(tagType, i);
562 } else {
563 int[] ints = new int[componentCount];
564 for (int i = 0; i<componentCount; i++)
565 ints[i] = get16Bits(tagValueOffset + (i * 2));
566 directory.setIntArray(tagType, ints);
567 }
568 break;
569 case FMT_SLONG:
570 case FMT_ULONG:
571 if (componentCount==1) {
572 int i = get32Bits(tagValueOffset);
573 directory.setInt(tagType, i);
574 } else {
575 int[] ints = new int[componentCount];
576 for (int i = 0; i<componentCount; i++)
577 ints[i] = get32Bits(tagValueOffset + (i * 4));
578 directory.setIntArray(tagType, ints);
579 }
580 break;
581 default:
582 directory.addError("Unknown format code " + formatCode + " for tag " + tagType);
583 }
584 }
585
586 private int calculateTagValueOffset(int byteCount, int dirEntryOffset, int tiffHeaderOffset)
587 {
588 if (byteCount>4) {
589 // If its bigger than 4 bytes, the dir entry contains an offset.
590 // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an
591 // offset relative to the start of the makernote itself, not the TIFF segment.
592 final int offsetVal = get32Bits(dirEntryOffset + 8);
593 if (offsetVal + byteCount>_data.length) {
594 // Bogus pointer offset and / or bytecount value
595 return -1; // signal error
596 }
597 return tiffHeaderOffset + offsetVal;
598 } else {
599 // 4 bytes or less and value is in the dir entry itself
600 return dirEntryOffset + 8;
601 }
602 }
603
604 /**
605 * Creates a String from the _data buffer starting at the specified offset,
606 * and ending where byte=='\0' or where length==maxLength.
607 */
608 private String readString(int offset, int maxLength)
609 {
610 int length = 0;
611 while ((offset + length)<_data.length && _data[offset + length]!='\0' && length<maxLength)
612 length++;
613
614 return new String(_data, offset, length);
615 }
616
617 /**
618 * Determine the offset at which a given InteropArray entry begins within the specified IFD.
619 * @param dirStartOffset the offset at which the IFD starts
620 * @param entryNumber the zero-based entry number
621 */
622 private int calculateTagOffset(int dirStartOffset, int entryNumber)
623 {
624 // add 2 bytes for the tag count
625 // each entry is 12 bytes, so we skip 12 * the number seen so far
626 return dirStartOffset + 2 + (12 * entryNumber);
627 }
628
629 /**
630 * Get a 16 bit value from file's native byte order. Between 0x0000 and 0xFFFF.
631 */
632 private int get16Bits(int offset)
633 {
634 if (offset<0 || offset+2>_data.length)
635 throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");
636
637 if (_isMotorollaByteOrder) {
638 // Motorola - MSB first
639 return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF);
640 } else {
641 // Intel ordering - LSB first
642 return (_data[offset + 1] << 8 & 0xFF00) | (_data[offset] & 0xFF);
643 }
644 }
645
646 /**
647 * Get a 32 bit value from file's native byte order.
648 */
649 private int get32Bits(int offset)
650 {
651 if (offset<0 || offset+4>_data.length)
652 throw new ArrayIndexOutOfBoundsException("attempt to read data outside of exif segment (index " + offset + " where max index is " + (_data.length - 1) + ")");
653
654 if (_isMotorollaByteOrder) {
655 // Motorola - MSB first
656 return (_data[offset] << 24 & 0xFF000000) |
657 (_data[offset + 1] << 16 & 0xFF0000) |
658 (_data[offset + 2] << 8 & 0xFF00) |
659 (_data[offset + 3] & 0xFF);
660 } else {
661 // Intel ordering - LSB first
662 return (_data[offset + 3] << 24 & 0xFF000000) |
663 (_data[offset + 2] << 16 & 0xFF0000) |
664 (_data[offset + 1] << 8 & 0xFF00) |
665 (_data[offset] & 0xFF);
666 }
667 }
668}
Note: See TracBrowser for help on using the repository browser.