Changeset 6127 in josm for trunk/src/com/drew/metadata/exif/ExifReader.java
- Timestamp:
- 2013-08-09T18:05:11+02:00 (13 years ago)
- File:
-
- 1 edited
-
trunk/src/com/drew/metadata/exif/ExifReader.java (modified) (11 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/com/drew/metadata/exif/ExifReader.java
r4231 r6127 1 1 /* 2 * EXIFExtractor.java2 * Copyright 2002-2012 Drew Noakes 3 3 * 4 * This class based upon code from Jhead, a C program for extracting and5 * manipulating the Exif data within files written by Matthias Wandel.6 * http://www.sentex.net/~mwandel/jhead/4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 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. 8 * http://www.apache.org/licenses/LICENSE-2.0 13 9 * 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. 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 18 15 * 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/ 16 * More information about this project is available at: 23 17 * 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. 18 * http://drewnoakes.com/code/exif/ 19 * http://code.google.com/p/metadata-extractor/ 43 20 */ 44 21 package com.drew.metadata.exif; 45 22 46 import com.drew.imaging.jpeg.JpegProcessingException; 47 import com.drew.imaging.jpeg.JpegSegmentData; 48 import com.drew.imaging.jpeg.JpegSegmentReader; 23 import com.drew.lang.BufferBoundsException; 24 import com.drew.lang.BufferReader; 49 25 import com.drew.lang.Rational; 26 import com.drew.lang.annotations.NotNull; 50 27 import com.drew.metadata.Directory; 51 28 import com.drew.metadata.Metadata; 52 29 import com.drew.metadata.MetadataReader; 53 30 54 import java.io.File; 55 import java.io.InputStream; 56 import java.util.HashMap; 31 import java.util.HashSet; 32 import java.util.Set; 57 33 58 34 /** 59 * Extracts Exifdata from a JPEG header segment, providing information about the60 * camera/scanner/capture device (if available). Information is encapsulated in61 * an <code>Metadata</code> object.62 * @author Drew Noakes http://drewnoakes.com35 * Decodes Exif binary data, populating a {@link Metadata} object with tag values in {@link ExifSubIFDDirectory}, 36 * {@link ExifThumbnailDirectory}, {@link ExifInteropDirectory}, {@link GpsDirectory} and one of the many camera makernote directories. 37 * 38 * @author Drew Noakes http://drewnoakes.com 63 39 */ 64 40 public class ExifReader implements MetadataReader 65 41 { 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 */ 42 // TODO extract a reusable TiffReader from this class with hooks for special tag handling and subdir following 43 44 /** The number of bytes used per format descriptor. */ 45 @NotNull 46 private static final int[] BYTES_PER_FORMAT = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 }; 47 48 /** The number of formats known. */ 92 49 private static final int MAX_FORMAT_CODE = 12; 93 50 94 51 // Format types 95 // Note: Cannot use the DataFormat enumeration in the case statement that uses these tags.96 / / Is there a better way?52 // TODO use an enum for these? 53 /** An 8-bit unsigned integer. */ 97 54 private static final int FMT_BYTE = 1; 55 /** A fixed-length character string. */ 98 56 private static final int FMT_STRING = 2; 57 /** An unsigned 16-bit integer. */ 99 58 private static final int FMT_USHORT = 3; 59 /** An unsigned 32-bit integer. */ 100 60 private static final int FMT_ULONG = 4; 101 61 private static final int FMT_URATIONAL = 5; 62 /** An 8-bit signed integer. */ 102 63 private static final int FMT_SBYTE = 6; 103 64 private static final int FMT_UNDEFINED = 7; 65 /** A signed 16-bit integer. */ 104 66 private static final int FMT_SSHORT = 8; 67 /** A signed 32-bit integer. */ 105 68 private static final int FMT_SLONG = 9; 106 69 private static final int FMT_SRATIONAL = 10; 70 /** A 32-bit floating point number. */ 107 71 private static final int FMT_SINGLE = 11; 72 /** A 64-bit floating point number. */ 108 73 private static final int FMT_DOUBLE = 12; 109 74 110 public static final int TAG_EXIF_OFFSET = 0x8769; 75 /** This tag is a pointer to the Exif SubIFD. */ 76 public static final int TAG_EXIF_SUB_IFD_OFFSET = 0x8769; 77 /** This tag is a pointer to the Exif Interop IFD. */ 111 78 public static final int TAG_INTEROP_OFFSET = 0xA005; 79 /** This tag is a pointer to the Exif GPS IFD. */ 112 80 public static final int TAG_GPS_INFO_OFFSET = 0x8825; 113 public static final int TAG_MAKER_NOTE = 0x927C; 81 /** This tag is a pointer to the Exif Makernote IFD. */ 82 public static final int TAG_MAKER_NOTE_OFFSET = 0x927C; 114 83 115 84 public static final int TIFF_HEADER_START_OFFSET = 6; 116 117 /**118 * Creates an ExifReader for a JpegSegmentData object.119 * @param segmentData120 */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 file129 * @throws JpegProcessingException130 */131 public ExifReader(File file) throws JpegProcessingException132 {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 JpegProcessingException141 {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 85 161 86 /** 162 87 * Performs the Exif data extraction, adding found values to the specified 163 88 * instance of <code>Metadata</code>. 89 * 90 * @param reader The buffer reader from which Exif data should be read. 91 * @param metadata The Metadata object into which extracted values should be merged. 164 92 */ 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); 93 public void extract(@NotNull final BufferReader reader, @NotNull Metadata metadata) 94 { 95 final ExifSubIFDDirectory directory = metadata.getOrCreateDirectory(ExifSubIFDDirectory.class); 173 96 174 97 // check for the header length 175 if ( _data.length<=14) {98 if (reader.getLength() <= 14) { 176 99 directory.addError("Exif data segment must contain at least 14 bytes"); 177 return _metadata;100 return; 178 101 } 179 102 180 103 // 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 104 try { 105 if (!reader.getString(0, 6).equals("Exif\0\0")) { 106 directory.addError("Exif data segment doesn't begin with 'Exif'"); 107 return; 108 } 109 110 extractIFD(metadata, metadata.getOrCreateDirectory(ExifIFD0Directory.class), TIFF_HEADER_START_OFFSET, reader); 111 } catch (BufferBoundsException e) { 112 directory.addError("Exif data segment ended prematurely"); 113 } 114 } 115 116 /** 117 * Performs the Exif data extraction on a TIFF/RAW, adding found values to the specified 118 * instance of <code>Metadata</code>. 119 * 120 * @param reader The BufferReader from which TIFF data should be read. 121 * @param metadata The Metadata object into which extracted values should be merged. 122 */ 123 public void extractTiff(@NotNull BufferReader reader, @NotNull Metadata metadata) 124 { 125 final ExifIFD0Directory directory = metadata.getOrCreateDirectory(ExifIFD0Directory.class); 126 127 try { 128 extractIFD(metadata, directory, 0, reader); 129 } catch (BufferBoundsException e) { 130 directory.addError("Exif data segment ended prematurely"); 131 } 132 } 133 134 private void extractIFD(@NotNull Metadata metadata, @NotNull final ExifIFD0Directory directory, int tiffHeaderOffset, @NotNull BufferReader reader) throws BufferBoundsException 135 { 186 136 // this should be either "MM" or "II" 187 String byteOrderIdentifier = new String(_data, 6, 2); 188 if (!setByteOrder(byteOrderIdentifier)) { 137 String byteOrderIdentifier = reader.getString(tiffHeaderOffset, 2); 138 139 if ("MM".equals(byteOrderIdentifier)) { 140 reader.setMotorolaByteOrder(true); 141 } else if ("II".equals(byteOrderIdentifier)) { 142 reader.setMotorolaByteOrder(false); 143 } else { 189 144 directory.addError("Unclear distinction between Motorola/Intel byte ordering: " + byteOrderIdentifier); 190 return _metadata;145 return; 191 146 } 192 147 193 148 // 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) { 149 final int tiffMarker = reader.getUInt16(2 + tiffHeaderOffset); 150 151 final int standardTiffMarker = 0x002A; 152 final int olympusRawTiffMarker = 0x4F52; // for ORF files 153 final int panasonicRawTiffMarker = 0x0055; // for RW2 files 154 155 if (tiffMarker != standardTiffMarker && tiffMarker != olympusRawTiffMarker && tiffMarker != panasonicRawTiffMarker) { 156 directory.addError("Unexpected TIFF marker after byte order identifier: 0x" + Integer.toHexString(tiffMarker)); 157 return; 158 } 159 160 int firstDirectoryOffset = reader.getInt32(4 + tiffHeaderOffset) + tiffHeaderOffset; 161 162 // David Ekholm sent a digital camera image that has this problem 163 if (firstDirectoryOffset >= reader.getLength() - 1) { 203 164 directory.addError("First exif directory offset is beyond end of Exif data segment"); 204 165 // First directory normally starts 14 bytes in -- try it here and catch another error in the worst case … … 206 167 } 207 168 208 HashMap processedDirectoryOffsets = new HashMap(); 209 210 // 0th IFD (we merge with Exif IFD) 211 processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, TIFF_HEADER_START_OFFSET); 169 Set<Integer> processedDirectoryOffsets = new HashSet<Integer>(); 170 171 processDirectory(directory, processedDirectoryOffsets, firstDirectoryOffset, tiffHeaderOffset, metadata, reader); 212 172 213 173 // 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; 174 ExifThumbnailDirectory thumbnailDirectory = metadata.getDirectory(ExifThumbnailDirectory.class); 175 if (thumbnailDirectory!=null && thumbnailDirectory.containsTag(ExifThumbnailDirectory.TAG_THUMBNAIL_COMPRESSION)) { 176 Integer offset = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_OFFSET); 177 Integer length = thumbnailDirectory.getInteger(ExifThumbnailDirectory.TAG_THUMBNAIL_LENGTH); 178 if (offset != null && length != null) { 179 try { 180 byte[] thumbnailData = reader.getBytes(tiffHeaderOffset + offset, length); 181 thumbnailDirectory.setThumbnailData(thumbnailData); 182 } catch (BufferBoundsException ex) { 183 directory.addError("Invalid thumbnail data specification: " + ex.getMessage()); 184 } 185 } 186 } 251 187 } 252 188 253 189 /** 254 190 * Process one of the nested Tiff IFD directories. 191 * <p/> 192 * Header 255 193 * 2 bytes: number of tags 256 * for each tag 257 * 2 bytes: tag type 258 * 2 bytes: format code 259 * 4 bytes: component count 194 * <p/> 195 * Then for each tag 196 * 2 bytes: tag type 197 * 2 bytes: format code 198 * 4 bytes: component count 260 199 */ 261 private void processDirectory(Directory directory, HashMap processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset)200 private void processDirectory(@NotNull Directory directory, @NotNull Set<Integer> processedDirectoryOffsets, int dirStartOffset, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull final BufferReader reader) throws BufferBoundsException 262 201 { 263 202 // check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist 264 if (processedDirectoryOffsets.contains Key(newInteger(dirStartOffset)))203 if (processedDirectoryOffsets.contains(Integer.valueOf(dirStartOffset))) 265 204 return; 266 205 267 206 // 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)) { 207 processedDirectoryOffsets.add(dirStartOffset); 208 209 if (dirStartOffset >= reader.getLength() || dirStartOffset < 0) { 210 directory.addError("Ignored directory marked to start outside data segment"); 211 return; 212 } 213 214 // First two bytes in the IFD are the number of tags in this directory 215 int dirTagCount = reader.getUInt16(dirStartOffset); 216 217 int dirLength = (2 + (12 * dirTagCount) + 4); 218 if (dirLength + dirStartOffset > reader.getLength()) { 276 219 directory.addError("Illegally sized directory"); 277 220 return; 278 221 } 279 222 280 // First two bytes in the IFD are the number of tags in this directory281 int dirTagCount = get16Bits(dirStartOffset);282 283 223 // Handle each tag in this directory 284 for (int tagNumber = 0; tagNumber<dirTagCount; tagNumber++) 285 { 224 for (int tagNumber = 0; tagNumber < dirTagCount; tagNumber++) { 286 225 final int tagOffset = calculateTagOffset(dirStartOffset, tagNumber); 287 226 288 227 // 2 bytes for the tag type 289 final int tagType = get16Bits(tagOffset);228 final int tagType = reader.getUInt16(tagOffset); 290 229 291 230 // 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; 231 final int formatCode = reader.getUInt16(tagOffset + 2); 232 if (formatCode < 1 || formatCode > MAX_FORMAT_CODE) { 233 // This error suggests that we are processing at an incorrect index and will generate 234 // rubbish until we go out of bounds (which may be a while). Exit now. 235 directory.addError("Invalid TIFF tag format code: " + formatCode); 236 return; 296 237 } 297 238 298 239 // 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");240 final int componentCount = reader.getInt32(tagOffset + 4); 241 if (componentCount < 0) { 242 directory.addError("Negative TIFF tag component count"); 302 243 continue; 303 244 } 304 245 // each component may have more than one byte... calculate the total number of bytes 305 246 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"); 247 final int tagValueOffset; 248 if (byteCount > 4) { 249 // If it's bigger than 4 bytes, the dir entry contains an offset. 250 // dirEntryOffset must be passed, as some makernote implementations (e.g. FujiFilm) incorrectly use an 251 // offset relative to the start of the makernote itself, not the TIFF segment. 252 final int offsetVal = reader.getInt32(tagOffset + 8); 253 if (offsetVal + byteCount > reader.getLength()) { 254 // Bogus pointer offset and / or byteCount value 255 directory.addError("Illegal TIFF tag pointer offset"); 256 continue; 257 } 258 tagValueOffset = tiffHeaderOffset + offsetVal; 259 } else { 260 // 4 bytes or less and value is in the dir entry itself 261 tagValueOffset = tagOffset + 8; 262 } 263 264 if (tagValueOffset < 0 || tagValueOffset > reader.getLength()) { 265 directory.addError("Illegal TIFF tag pointer offset"); 309 266 continue; 310 267 } … … 312 269 // Check that this tag isn't going to allocate outside the bounds of the data array. 313 270 // This addresses an uncommon OutOfMemoryError. 314 if (byteCount < 0 || tagValueOffset + byteCount > _data.length) 315 { 271 if (byteCount < 0 || tagValueOffset + byteCount > reader.getLength()) { 316 272 directory.addError("Illegal number of bytes: " + byteCount); 317 273 continue; 318 274 } 319 275 320 // Calculate the value as an offset for cases where the tag represents directory321 final int subdirOffset = tiffHeaderOffset + get32Bits(tagValueOffset);322 323 276 switch (tagType) { 324 case TAG_EXIF_OFFSET: 325 processDirectory(_metadata.getDirectory(ExifDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 277 case TAG_EXIF_SUB_IFD_OFFSET: { 278 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 279 processDirectory(metadata.getOrCreateDirectory(ExifSubIFDDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 326 280 continue; 327 case TAG_INTEROP_OFFSET: 328 processDirectory(_metadata.getDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 281 } 282 case TAG_INTEROP_OFFSET: { 283 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 284 processDirectory(metadata.getOrCreateDirectory(ExifInteropDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 329 285 continue; 330 case TAG_GPS_INFO_OFFSET: 331 processDirectory(_metadata.getDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset); 286 } 287 case TAG_GPS_INFO_OFFSET: { 288 final int subdirOffset = tiffHeaderOffset + reader.getInt32(tagValueOffset); 289 processDirectory(metadata.getOrCreateDirectory(GpsDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 332 290 continue; 333 case TAG_MAKER_NOTE: 334 processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset); 291 } 292 case TAG_MAKER_NOTE_OFFSET: { 293 processMakerNote(tagValueOffset, processedDirectoryOffsets, tiffHeaderOffset, metadata, reader); 335 294 continue; 336 default: 337 processTag(directory, tagType, tagValueOffset, componentCount, formatCode); 295 } 296 default: { 297 processTag(directory, tagType, tagValueOffset, componentCount, formatCode, reader); 338 298 break; 299 } 339 300 } 340 301 } … … 342 303 // at the end of each IFD is an optional link to the next IFD 343 304 final int finalTagOffset = calculateTagOffset(dirStartOffset, dirTagCount); 344 int nextDirectoryOffset = get32Bits(finalTagOffset);345 if (nextDirectoryOffset !=0) {305 int nextDirectoryOffset = reader.getInt32(finalTagOffset); 306 if (nextDirectoryOffset != 0) { 346 307 nextDirectoryOffset += tiffHeaderOffset; 347 if (nextDirectoryOffset >=_data.length) {308 if (nextDirectoryOffset >= reader.getLength()) { 348 309 // Last 4 bytes of IFD reference another IFD with an address that is out of bounds 349 310 // Note this could have been caused by jhead 1.3 cropping too much … … 353 314 return; 354 315 } 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) 316 // TODO in Exif, the only known 'follower' IFD is the thumbnail one, however this may not be the case 317 final ExifThumbnailDirectory nextDirectory = metadata.getOrCreateDirectory(ExifThumbnailDirectory.class); 318 processDirectory(nextDirectory, processedDirectoryOffsets, nextDirectoryOffset, tiffHeaderOffset, metadata, reader); 319 } 320 } 321 322 private void processMakerNote(int subdirOffset, @NotNull Set<Integer> processedDirectoryOffsets, int tiffHeaderOffset, @NotNull final Metadata metadata, @NotNull BufferReader reader) throws BufferBoundsException 361 323 { 362 324 // 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 { 325 Directory ifd0Directory = metadata.getDirectory(ExifIFD0Directory.class); 326 327 if (ifd0Directory==null) 328 return; 329 330 String cameraModel = ifd0Directory.getString(ExifIFD0Directory.TAG_MAKE); 331 332 //final String firstTwoChars = reader.getString(subdirOffset, 2); 333 final String firstThreeChars = reader.getString(subdirOffset, 3); 334 final String firstFourChars = reader.getString(subdirOffset, 4); 335 final String firstFiveChars = reader.getString(subdirOffset, 5); 336 final String firstSixChars = reader.getString(subdirOffset, 6); 337 final String firstSevenChars = reader.getString(subdirOffset, 7); 338 final String firstEightChars = reader.getString(subdirOffset, 8); 339 final String firstTwelveChars = reader.getString(subdirOffset, 12); 340 341 if ("OLYMP".equals(firstFiveChars) || "EPSON".equals(firstFiveChars) || "AGFA".equals(firstFourChars)) { 378 342 // 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 { 343 // Epson and Agfa use Olympus maker note standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ 344 processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader); 345 } else if (cameraModel != null && cameraModel.trim().toUpperCase().startsWith("NIKON")) { 346 if ("Nikon".equals(firstFiveChars)) { 387 347 /* There are two scenarios here: 388 348 * Type 1: ** … … 393 353 * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 394 354 */ 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 } 355 switch (reader.getUInt8(subdirOffset + 6)) { 356 case 1: 357 processDirectory(metadata.getOrCreateDirectory(NikonType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 8, tiffHeaderOffset, metadata, reader); 358 break; 359 case 2: 360 processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 18, subdirOffset + 10, metadata, reader); 361 break; 362 default: 363 ifd0Directory.addError("Unsupported Nikon makernote data ignored."); 364 break; 365 } 366 } else { 367 // The IFD begins with the first MakerNote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models. 368 processDirectory(metadata.getOrCreateDirectory(NikonType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 369 } 370 } else if ("SONY CAM".equals(firstEightChars) || "SONY DSC".equals(firstEightChars)) { 371 processDirectory(metadata.getOrCreateDirectory(SonyType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader); 372 } else if ("SIGMA\u0000\u0000\u0000".equals(firstEightChars) || "FOVEON\u0000\u0000".equals(firstEightChars)) { 373 processDirectory(metadata.getOrCreateDirectory(SigmaMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 10, tiffHeaderOffset, metadata, reader); 374 } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000".equals(firstTwelveChars)) { 375 // force MM for this directory 376 boolean isMotorola = reader.isMotorolaByteOrder(); 377 reader.setMotorolaByteOrder(true); 378 // skip 12 byte header + 2 for "MM" + 6 379 processDirectory(metadata.getOrCreateDirectory(SonyType6MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader); 380 reader.setMotorolaByteOrder(isMotorola); 381 } else if ("KDK".equals(firstThreeChars)) { 382 processDirectory(metadata.getOrCreateDirectory(KodakMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 20, tiffHeaderOffset, metadata, reader); 383 } else if ("Canon".equalsIgnoreCase(cameraModel)) { 384 processDirectory(metadata.getOrCreateDirectory(CanonMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 385 } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("CASIO")) { 386 if ("QVC\u0000\u0000\u0000".equals(firstSixChars)) 387 processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, tiffHeaderOffset, metadata, reader); 402 388 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; 389 processDirectory(metadata.getOrCreateDirectory(CasioType1MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 390 } else if ("FUJIFILM".equals(firstEightChars) || "Fujifilm".equalsIgnoreCase(cameraModel)) { 391 boolean byteOrderBefore = reader.isMotorolaByteOrder(); 431 392 // bug in fujifilm makernote ifd means we temporarily use Intel byte ordering 432 _isMotorollaByteOrder= false;393 reader.setMotorolaByteOrder(false); 433 394 // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote 434 395 // IFD, though the offset is relative to the start of the makernote, not the TIFF 435 396 // 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 { 397 int ifdStart = subdirOffset + reader.getInt32(subdirOffset + 8); 398 processDirectory(metadata.getOrCreateDirectory(FujifilmMakernoteDirectory.class), processedDirectoryOffsets, ifdStart, tiffHeaderOffset, metadata, reader); 399 reader.setMotorolaByteOrder(byteOrderBefore); 400 } else if (cameraModel != null && cameraModel.toUpperCase().startsWith("MINOLTA")) { 442 401 // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote 443 402 // 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 { 403 processDirectory(metadata.getOrCreateDirectory(OlympusMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, tiffHeaderOffset, metadata, reader); 404 } else if ("KYOCERA".equals(firstSevenChars)) { 455 405 // 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 { 406 processDirectory(metadata.getOrCreateDirectory(KyoceraMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 22, tiffHeaderOffset, metadata, reader); 407 } else if ("Panasonic\u0000\u0000\u0000".equals(reader.getString(subdirOffset, 12))) { 460 408 // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD 461 409 // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment 462 410 // 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 { 411 processDirectory(metadata.getOrCreateDirectory(PanasonicMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 12, tiffHeaderOffset, metadata, reader); 412 } else if ("AOC\u0000".equals(firstFourChars)) { 467 413 // NON-Standard TIFF IFD Data using Casio Type 2 Tags 468 414 // IFD has no Next-IFD pointer at end of IFD, and … … 470 416 // Observed for: 471 417 // - 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 { 418 processDirectory(metadata.getOrCreateDirectory(CasioType2MakernoteDirectory.class), processedDirectoryOffsets, subdirOffset + 6, subdirOffset, metadata, reader); 419 } else if (cameraModel != null && (cameraModel.toUpperCase().startsWith("PENTAX") || cameraModel.toUpperCase().startsWith("ASAHI"))) { 476 420 // NON-Standard TIFF IFD Data using Pentax Tags 477 421 // IFD has no Next-IFD pointer at end of IFD, and … … 480 424 // - PENTAX Optio 330 481 425 // - PENTAX Optio 430 482 processDirectory(_metadata.getDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset); 483 } 484 else 485 { 426 processDirectory(metadata.getOrCreateDirectory(PentaxMakernoteDirectory.class), processedDirectoryOffsets, subdirOffset, subdirOffset, metadata, reader); 427 // } else if ("KC".equals(firstTwoChars) || "MINOL".equals(firstFiveChars) || "MLY".equals(firstThreeChars) || "+M+M+M+M".equals(firstEightChars)) { 428 // // This Konica data is not understood. Header identified in accordance with information at this site: 429 // // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html 430 // // TODO add support for minolta/konica cameras 431 // exifDirectory.addError("Unsupported Konica/Minolta data ignored."); 432 } else { 486 433 // TODO how to store makernote data when it's not from a supported camera model? 487 434 // 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) 435 } 436 } 437 438 private void processTag(@NotNull Directory directory, int tagType, int tagValueOffset, int componentCount, int formatCode, @NotNull final BufferReader reader) throws BufferBoundsException 504 439 { 505 440 // Directory simply stores raw values 506 441 // The display side uses a Descriptor class per directory to turn the raw values into 'pretty' descriptions 507 switch (formatCode) 508 { 442 switch (formatCode) { 509 443 case FMT_UNDEFINED: 510 444 // 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); 445 directory.setByteArray(tagType, reader.getBytes(tagValueOffset, componentCount)); 516 446 break; 517 447 case FMT_STRING: 518 directory.setString(tagType, readString(tagValueOffset, componentCount)); 448 String string = reader.getNullTerminatedString(tagValueOffset, componentCount); 449 directory.setString(tagType, string); 450 /* 451 // special handling for certain known tags, proposed by Yuri Binev but left out for now, 452 // as it gives the false impression that the image was captured in the same timezone 453 // in which the string is parsed 454 if (tagType==ExifSubIFDDirectory.TAG_DATETIME || 455 tagType==ExifSubIFDDirectory.TAG_DATETIME_ORIGINAL || 456 tagType==ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED) { 457 String[] datePatterns = { 458 "yyyy:MM:dd HH:mm:ss", 459 "yyyy:MM:dd HH:mm", 460 "yyyy-MM-dd HH:mm:ss", 461 "yyyy-MM-dd HH:mm"}; 462 for (String datePattern : datePatterns) { 463 try { 464 DateFormat parser = new SimpleDateFormat(datePattern); 465 Date date = parser.parse(string); 466 directory.setDate(tagType, date); 467 break; 468 } catch (ParseException ex) { 469 // simply try the next pattern 470 } 471 } 472 } 473 */ 519 474 break; 520 475 case FMT_SRATIONAL: 476 if (componentCount == 1) { 477 directory.setRational(tagType, new Rational(reader.getInt32(tagValueOffset), reader.getInt32(tagValueOffset + 4))); 478 } else if (componentCount > 1) { 479 Rational[] rationals = new Rational[componentCount]; 480 for (int i = 0; i < componentCount; i++) 481 rationals[i] = new Rational(reader.getInt32(tagValueOffset + (8 * i)), reader.getInt32(tagValueOffset + 4 + (8 * i))); 482 directory.setRationalArray(tagType, rationals); 483 } 484 break; 521 485 case FMT_URATIONAL: 522 if (componentCount==1) { 523 Rational rational = new Rational(get32Bits(tagValueOffset), get32Bits(tagValueOffset + 4)); 524 directory.setRational(tagType, rational); 525 } else { 486 if (componentCount == 1) { 487 directory.setRational(tagType, new Rational(reader.getUInt32(tagValueOffset), reader.getUInt32(tagValueOffset + 4))); 488 } else if (componentCount > 1) { 526 489 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)));490 for (int i = 0; i < componentCount; i++) 491 rationals[i] = new Rational(reader.getUInt32(tagValueOffset + (8 * i)), reader.getUInt32(tagValueOffset + 4 + (8 * i))); 529 492 directory.setRationalArray(tagType, rationals); 530 493 } 531 494 break; 495 case FMT_SINGLE: 496 if (componentCount == 1) { 497 directory.setFloat(tagType, reader.getFloat32(tagValueOffset)); 498 } else { 499 float[] floats = new float[componentCount]; 500 for (int i = 0; i < componentCount; i++) 501 floats[i] = reader.getFloat32(tagValueOffset + (i * 4)); 502 directory.setFloatArray(tagType, floats); 503 } 504 break; 505 case FMT_DOUBLE: 506 if (componentCount == 1) { 507 directory.setDouble(tagType, reader.getDouble64(tagValueOffset)); 508 } else { 509 double[] doubles = new double[componentCount]; 510 for (int i = 0; i < componentCount; i++) 511 doubles[i] = reader.getDouble64(tagValueOffset + (i * 4)); 512 directory.setDoubleArray(tagType, doubles); 513 } 514 break; 515 516 // 517 // Note that all integral types are stored as int32 internally (the largest supported by TIFF) 518 // 519 532 520 case FMT_SBYTE: 521 if (componentCount == 1) { 522 directory.setInt(tagType, reader.getInt8(tagValueOffset)); 523 } else { 524 int[] bytes = new int[componentCount]; 525 for (int i = 0; i < componentCount; i++) 526 bytes[i] = reader.getInt8(tagValueOffset + i); 527 directory.setIntArray(tagType, bytes); 528 } 529 break; 533 530 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); 531 if (componentCount == 1) { 532 directory.setInt(tagType, reader.getUInt8(tagValueOffset)); 538 533 } else { 539 534 int[] bytes = new int[componentCount]; 540 for (int i = 0; i <componentCount; i++)541 bytes[i] = _data[tagValueOffset + i];535 for (int i = 0; i < componentCount; i++) 536 bytes[i] = reader.getUInt8(tagValueOffset + i); 542 537 directory.setIntArray(tagType, bytes); 543 538 } 544 539 break; 545 case FMT_SINGLE: 546 case FMT_DOUBLE: 547 if (componentCount==1) { 548 int i = _data[tagValueOffset]; 540 case FMT_USHORT: 541 if (componentCount == 1) { 542 int i = reader.getUInt16(tagValueOffset); 549 543 directory.setInt(tagType, i); 550 544 } else { 551 545 int[] ints = new int[componentCount]; 552 for (int i = 0; i <componentCount; i++)553 ints[i] = _data[tagValueOffset +i];546 for (int i = 0; i < componentCount; i++) 547 ints[i] = reader.getUInt16(tagValueOffset + (i * 2)); 554 548 directory.setIntArray(tagType, ints); 555 549 } 556 550 break; 557 case FMT_USHORT:558 551 case FMT_SSHORT: 559 if (componentCount ==1) {560 int i = get16Bits(tagValueOffset);552 if (componentCount == 1) { 553 int i = reader.getInt16(tagValueOffset); 561 554 directory.setInt(tagType, i); 562 555 } else { 563 556 int[] ints = new int[componentCount]; 564 for (int i = 0; i <componentCount; i++)565 ints[i] = get16Bits(tagValueOffset + (i * 2));557 for (int i = 0; i < componentCount; i++) 558 ints[i] = reader.getInt16(tagValueOffset + (i * 2)); 566 559 directory.setIntArray(tagType, ints); 567 560 } … … 569 562 case FMT_SLONG: 570 563 case FMT_ULONG: 571 if (componentCount==1) { 572 int i = get32Bits(tagValueOffset); 564 // NOTE 'long' in this case means 32 bit, not 64 565 if (componentCount == 1) { 566 int i = reader.getInt32(tagValueOffset); 573 567 directory.setInt(tagType, i); 574 568 } else { 575 569 int[] ints = new int[componentCount]; 576 for (int i = 0; i <componentCount; i++)577 ints[i] = get32Bits(tagValueOffset + (i * 4));570 for (int i = 0; i < componentCount; i++) 571 ints[i] = reader.getInt32(tagValueOffset + (i * 4)); 578 572 directory.setIntArray(tagType, ints); 579 573 } … … 584 578 } 585 579 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 an591 // 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 value595 return -1; // signal error596 }597 return tiffHeaderOffset + offsetVal;598 } else {599 // 4 bytes or less and value is in the dir entry itself600 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 580 /** 618 581 * Determine the offset at which a given InteropArray entry begins within the specified IFD. 582 * 619 583 * @param dirStartOffset the offset at which the IFD starts 620 * @param entryNumber the zero-based entry number 584 * @param entryNumber the zero-based entry number 621 585 */ 622 586 private int calculateTagOffset(int dirStartOffset, int entryNumber) … … 626 590 return dirStartOffset + 2 + (12 * entryNumber); 627 591 } 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 first639 return (_data[offset] << 8 & 0xFF00) | (_data[offset + 1] & 0xFF);640 } else {641 // Intel ordering - LSB first642 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 first656 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 first662 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 592 }
Note:
See TracChangeset
for help on using the changeset viewer.
