2 * Copyright (C) 2007 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.media;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.util.Log;
22 import android.util.Pair;
24 import libcore.io.IoUtils;
25 import libcore.io.Streams;
27 import java.io.BufferedInputStream;
28 import java.io.ByteArrayInputStream;
29 import java.io.DataInputStream;
30 import java.io.EOFException;
32 import java.io.FileDescriptor;
33 import java.io.FileInputStream;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.io.FilterOutputStream;
37 import java.io.IOException;
38 import java.io.InputStream;
39 import java.io.OutputStream;
40 import java.nio.ByteBuffer;
41 import java.nio.ByteOrder;
42 import java.nio.charset.Charset;
43 import java.text.ParsePosition;
44 import java.text.SimpleDateFormat;
45 import java.util.Arrays;
46 import java.util.Date;
47 import java.util.HashMap;
48 import java.util.HashSet;
51 import java.util.TimeZone;
52 import java.util.regex.Matcher;
53 import java.util.regex.Pattern;
56 * This is a class for reading and writing Exif tags in a JPEG file.
58 public class ExifInterface {
59 private static final String TAG = "ExifInterface";
60 private static final boolean DEBUG = false;
63 /** Type is String. @hide */
64 public static final String TAG_ARTIST = "Artist";
65 /** Type is int. @hide */
66 public static final String TAG_BITS_PER_SAMPLE = "BitsPerSample";
67 /** Type is int. @hide */
68 public static final String TAG_COMPRESSION = "Compression";
69 /** Type is String. @hide */
70 public static final String TAG_COPYRIGHT = "Copyright";
71 /** Type is String. */
72 public static final String TAG_DATETIME = "DateTime";
73 /** Type is String. @hide */
74 public static final String TAG_IMAGE_DESCRIPTION = "ImageDescription";
76 public static final String TAG_IMAGE_LENGTH = "ImageLength";
78 public static final String TAG_IMAGE_WIDTH = "ImageWidth";
79 /** Type is int. @hide */
80 public static final String TAG_JPEG_INTERCHANGE_FORMAT = "JPEGInterchangeFormat";
81 /** Type is int. @hide */
82 public static final String TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = "JPEGInterchangeFormatLength";
83 /** Type is String. */
84 public static final String TAG_MAKE = "Make";
85 /** Type is String. */
86 public static final String TAG_MODEL = "Model";
88 public static final String TAG_ORIENTATION = "Orientation";
89 /** Type is int. @hide */
90 public static final String TAG_PHOTOMETRIC_INTERPRETATION = "PhotometricInterpretation";
91 /** Type is int. @hide */
92 public static final String TAG_PLANAR_CONFIGURATION = "PlanarConfiguration";
93 /** Type is rational. @hide */
94 public static final String TAG_PRIMARY_CHROMATICITIES = "PrimaryChromaticities";
95 /** Type is rational. @hide */
96 public static final String TAG_REFERENCE_BLACK_WHITE = "ReferenceBlackWhite";
97 /** Type is int. @hide */
98 public static final String TAG_RESOLUTION_UNIT = "ResolutionUnit";
99 /** Type is int. @hide */
100 public static final String TAG_ROWS_PER_STRIP = "RowsPerStrip";
101 /** Type is int. @hide */
102 public static final String TAG_SAMPLES_PER_PIXEL = "SamplesPerPixel";
103 /** Type is String. @hide */
104 public static final String TAG_SOFTWARE = "Software";
105 /** Type is int. @hide */
106 public static final String TAG_STRIP_BYTE_COUNTS = "StripByteCounts";
107 /** Type is int. @hide */
108 public static final String TAG_STRIP_OFFSETS = "StripOffsets";
109 /** Type is int. @hide */
110 public static final String TAG_TRANSFER_FUNCTION = "TransferFunction";
111 /** Type is rational. @hide */
112 public static final String TAG_WHITE_POINT = "WhitePoint";
113 /** Type is rational. @hide */
114 public static final String TAG_X_RESOLUTION = "XResolution";
115 /** Type is rational. @hide */
116 public static final String TAG_Y_CB_CR_COEFFICIENTS = "YCbCrCoefficients";
117 /** Type is int. @hide */
118 public static final String TAG_Y_CB_CR_POSITIONING = "YCbCrPositioning";
119 /** Type is int. @hide */
120 public static final String TAG_Y_CB_CR_SUB_SAMPLING = "YCbCrSubSampling";
121 /** Type is rational. @hide */
122 public static final String TAG_Y_RESOLUTION = "YResolution";
123 /** Type is rational. @hide */
124 public static final String TAG_APERTURE_VALUE = "ApertureValue";
125 /** Type is rational. @hide */
126 public static final String TAG_BRIGHTNESS_VALUE = "BrightnessValue";
127 /** Type is String. @hide */
128 public static final String TAG_CFA_PATTERN = "CFAPattern";
129 /** Type is int. @hide */
130 public static final String TAG_COLOR_SPACE = "ColorSpace";
131 /** Type is String. @hide */
132 public static final String TAG_COMPONENTS_CONFIGURATION = "ComponentsConfiguration";
133 /** Type is rational. @hide */
134 public static final String TAG_COMPRESSED_BITS_PER_PIXEL = "CompressedBitsPerPixel";
135 /** Type is int. @hide */
136 public static final String TAG_CONTRAST = "Contrast";
137 /** Type is int. @hide */
138 public static final String TAG_CUSTOM_RENDERED = "CustomRendered";
139 /** Type is String. */
140 public static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
141 /** Type is String. @hide */
142 public static final String TAG_DATETIME_ORIGINAL = "DateTimeOriginal";
143 /** Type is String. @hide */
144 public static final String TAG_DEVICE_SETTING_DESCRIPTION = "DeviceSettingDescription";
145 /** Type is double. @hide */
146 public static final String TAG_DIGITAL_ZOOM_RATIO = "DigitalZoomRatio";
147 /** Type is String. @hide */
148 public static final String TAG_EXIF_VERSION = "ExifVersion";
149 /** Type is double. @hide */
150 public static final String TAG_EXPOSURE_BIAS_VALUE = "ExposureBiasValue";
151 /** Type is rational. @hide */
152 public static final String TAG_EXPOSURE_INDEX = "ExposureIndex";
153 /** Type is int. @hide */
154 public static final String TAG_EXPOSURE_MODE = "ExposureMode";
155 /** Type is int. @hide */
156 public static final String TAG_EXPOSURE_PROGRAM = "ExposureProgram";
157 /** Type is double. */
158 public static final String TAG_EXPOSURE_TIME = "ExposureTime";
159 /** Type is double. */
160 public static final String TAG_APERTURE = "FNumber";
161 /** Type is String. @hide */
162 public static final String TAG_FILE_SOURCE = "FileSource";
164 public static final String TAG_FLASH = "Flash";
165 /** Type is rational. @hide */
166 public static final String TAG_FLASH_ENERGY = "FlashEnergy";
167 /** Type is String. @hide */
168 public static final String TAG_FLASHPIX_VERSION = "FlashpixVersion";
169 /** Type is rational. */
170 public static final String TAG_FOCAL_LENGTH = "FocalLength";
171 /** Type is int. @hide */
172 public static final String TAG_FOCAL_LENGTH_IN_35MM_FILM = "FocalLengthIn35mmFilm";
173 /** Type is int. @hide */
174 public static final String TAG_FOCAL_PLANE_RESOLUTION_UNIT = "FocalPlaneResolutionUnit";
175 /** Type is rational. @hide */
176 public static final String TAG_FOCAL_PLANE_X_RESOLUTION = "FocalPlaneXResolution";
177 /** Type is rational. @hide */
178 public static final String TAG_FOCAL_PLANE_Y_RESOLUTION = "FocalPlaneYResolution";
179 /** Type is int. @hide */
180 public static final String TAG_GAIN_CONTROL = "GainControl";
182 public static final String TAG_ISO = "ISOSpeedRatings";
183 /** Type is String. @hide */
184 public static final String TAG_IMAGE_UNIQUE_ID = "ImageUniqueID";
185 /** Type is int. @hide */
186 public static final String TAG_LIGHT_SOURCE = "LightSource";
187 /** Type is String. @hide */
188 public static final String TAG_MAKER_NOTE = "MakerNote";
189 /** Type is rational. @hide */
190 public static final String TAG_MAX_APERTURE_VALUE = "MaxApertureValue";
191 /** Type is int. @hide */
192 public static final String TAG_METERING_MODE = "MeteringMode";
193 /** Type is String. @hide */
194 public static final String TAG_OECF = "OECF";
195 /** Type is int. @hide */
196 public static final String TAG_PIXEL_X_DIMENSION = "PixelXDimension";
197 /** Type is int. @hide */
198 public static final String TAG_PIXEL_Y_DIMENSION = "PixelYDimension";
199 /** Type is String. @hide */
200 public static final String TAG_RELATED_SOUND_FILE = "RelatedSoundFile";
201 /** Type is int. @hide */
202 public static final String TAG_SATURATION = "Saturation";
203 /** Type is int. @hide */
204 public static final String TAG_SCENE_CAPTURE_TYPE = "SceneCaptureType";
205 /** Type is String. @hide */
206 public static final String TAG_SCENE_TYPE = "SceneType";
207 /** Type is int. @hide */
208 public static final String TAG_SENSING_METHOD = "SensingMethod";
209 /** Type is int. @hide */
210 public static final String TAG_SHARPNESS = "Sharpness";
211 /** Type is rational. @hide */
212 public static final String TAG_SHUTTER_SPEED_VALUE = "ShutterSpeedValue";
213 /** Type is String. @hide */
214 public static final String TAG_SPATIAL_FREQUENCY_RESPONSE = "SpatialFrequencyResponse";
215 /** Type is String. @hide */
216 public static final String TAG_SPECTRAL_SENSITIVITY = "SpectralSensitivity";
217 /** Type is String. */
218 public static final String TAG_SUBSEC_TIME = "SubSecTime";
219 /** Type is String. */
220 public static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
221 /** Type is String. */
222 public static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
223 /** Type is int. @hide */
224 public static final String TAG_SUBJECT_AREA = "SubjectArea";
225 /** Type is double. @hide */
226 public static final String TAG_SUBJECT_DISTANCE = "SubjectDistance";
227 /** Type is int. @hide */
228 public static final String TAG_SUBJECT_DISTANCE_RANGE = "SubjectDistanceRange";
229 /** Type is int. @hide */
230 public static final String TAG_SUBJECT_LOCATION = "SubjectLocation";
231 /** Type is String. @hide */
232 public static final String TAG_USER_COMMENT = "UserComment";
234 public static final String TAG_WHITE_BALANCE = "WhiteBalance";
236 * The altitude (in meters) based on the reference in TAG_GPS_ALTITUDE_REF.
239 public static final String TAG_GPS_ALTITUDE = "GPSAltitude";
241 * 0 if the altitude is above sea level. 1 if the altitude is below sea
242 * level. Type is int.
244 public static final String TAG_GPS_ALTITUDE_REF = "GPSAltitudeRef";
245 /** Type is String. @hide */
246 public static final String TAG_GPS_AREA_INFORMATION = "GPSAreaInformation";
247 /** Type is rational. @hide */
248 public static final String TAG_GPS_DOP = "GPSDOP";
249 /** Type is String. */
250 public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
251 /** Type is rational. @hide */
252 public static final String TAG_GPS_DEST_BEARING = "GPSDestBearing";
253 /** Type is String. @hide */
254 public static final String TAG_GPS_DEST_BEARING_REF = "GPSDestBearingRef";
255 /** Type is rational. @hide */
256 public static final String TAG_GPS_DEST_DISTANCE = "GPSDestDistance";
257 /** Type is String. @hide */
258 public static final String TAG_GPS_DEST_DISTANCE_REF = "GPSDestDistanceRef";
259 /** Type is rational. @hide */
260 public static final String TAG_GPS_DEST_LATITUDE = "GPSDestLatitude";
261 /** Type is String. @hide */
262 public static final String TAG_GPS_DEST_LATITUDE_REF = "GPSDestLatitudeRef";
263 /** Type is rational. @hide */
264 public static final String TAG_GPS_DEST_LONGITUDE = "GPSDestLongitude";
265 /** Type is String. @hide */
266 public static final String TAG_GPS_DEST_LONGITUDE_REF = "GPSDestLongitudeRef";
267 /** Type is int. @hide */
268 public static final String TAG_GPS_DIFFERENTIAL = "GPSDifferential";
269 /** Type is rational. @hide */
270 public static final String TAG_GPS_IMG_DIRECTION = "GPSImgDirection";
271 /** Type is String. @hide */
272 public static final String TAG_GPS_IMG_DIRECTION_REF = "GPSImgDirectionRef";
273 /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
274 public static final String TAG_GPS_LATITUDE = "GPSLatitude";
275 /** Type is String. */
276 public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
277 /** Type is rational. Format is "num1/denom1,num2/denom2,num3/denom3". */
278 public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
279 /** Type is String. */
280 public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
281 /** Type is String. @hide */
282 public static final String TAG_GPS_MAP_DATUM = "GPSMapDatum";
283 /** Type is String. @hide */
284 public static final String TAG_GPS_MEASURE_MODE = "GPSMeasureMode";
285 /** Type is String. Name of GPS processing method used for location finding. */
286 public static final String TAG_GPS_PROCESSING_METHOD = "GPSProcessingMethod";
287 /** Type is String. @hide */
288 public static final String TAG_GPS_SATELLITES = "GPSSatellites";
289 /** Type is rational. @hide */
290 public static final String TAG_GPS_SPEED = "GPSSpeed";
291 /** Type is String. @hide */
292 public static final String TAG_GPS_SPEED_REF = "GPSSpeedRef";
293 /** Type is String. @hide */
294 public static final String TAG_GPS_STATUS = "GPSStatus";
295 /** Type is String. Format is "hh:mm:ss". */
296 public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
297 /** Type is rational. @hide */
298 public static final String TAG_GPS_TRACK = "GPSTrack";
299 /** Type is String. @hide */
300 public static final String TAG_GPS_TRACK_REF = "GPSTrackRef";
301 /** Type is String. @hide */
302 public static final String TAG_GPS_VERSION_ID = "GPSVersionID";
303 /** Type is String. @hide */
304 public static final String TAG_INTEROPERABILITY_INDEX = "InteroperabilityIndex";
305 /** Type is int. @hide */
306 public static final String TAG_THUMBNAIL_IMAGE_LENGTH = "ThumbnailImageLength";
307 /** Type is int. @hide */
308 public static final String TAG_THUMBNAIL_IMAGE_WIDTH = "ThumbnailImageWidth";
310 // Private tags used for pointing the other IFD offset. The types of the following tags are int.
311 private static final String TAG_EXIF_IFD_POINTER = "ExifIFDPointer";
312 private static final String TAG_GPS_INFO_IFD_POINTER = "GPSInfoIFDPointer";
313 private static final String TAG_INTEROPERABILITY_IFD_POINTER = "InteroperabilityIFDPointer";
315 // Private tags used for thumbnail information.
316 private static final String TAG_HAS_THUMBNAIL = "HasThumbnail";
317 private static final String TAG_THUMBNAIL_OFFSET = "ThumbnailOffset";
318 private static final String TAG_THUMBNAIL_LENGTH = "ThumbnailLength";
319 private static final String TAG_THUMBNAIL_DATA = "ThumbnailData";
321 // Constants used for the Orientation Exif tag.
322 public static final int ORIENTATION_UNDEFINED = 0;
323 public static final int ORIENTATION_NORMAL = 1;
324 public static final int ORIENTATION_FLIP_HORIZONTAL = 2; // left right reversed mirror
325 public static final int ORIENTATION_ROTATE_180 = 3;
326 public static final int ORIENTATION_FLIP_VERTICAL = 4; // upside down mirror
327 // flipped about top-left <--> bottom-right axis
328 public static final int ORIENTATION_TRANSPOSE = 5;
329 public static final int ORIENTATION_ROTATE_90 = 6; // rotate 90 cw to right it
330 // flipped about top-right <--> bottom-left axis
331 public static final int ORIENTATION_TRANSVERSE = 7;
332 public static final int ORIENTATION_ROTATE_270 = 8; // rotate 270 to right it
334 // Constants used for white balance
335 public static final int WHITEBALANCE_AUTO = 0;
336 public static final int WHITEBALANCE_MANUAL = 1;
338 private static SimpleDateFormat sFormatter;
340 // See Exchangeable image file format for digital still cameras: Exif version 2.2.
341 // The following values are for parsing EXIF data area. There are tag groups in EXIF data area.
342 // They are called "Image File Directory". They have multiple data formats to cover various
343 // image metadata from GPS longitude to camera model name.
345 // Types of Exif byte alignments (see JEITA CP-3451 page 10)
346 private static final short BYTE_ALIGN_II = 0x4949; // II: Intel order
347 private static final short BYTE_ALIGN_MM = 0x4d4d; // MM: Motorola order
349 // Formats for the value in IFD entry (See TIFF 6.0 spec Types page 15).
350 private static final int IFD_FORMAT_BYTE = 1;
351 private static final int IFD_FORMAT_STRING = 2;
352 private static final int IFD_FORMAT_USHORT = 3;
353 private static final int IFD_FORMAT_ULONG = 4;
354 private static final int IFD_FORMAT_URATIONAL = 5;
355 private static final int IFD_FORMAT_SBYTE = 6;
356 private static final int IFD_FORMAT_UNDEFINED = 7;
357 private static final int IFD_FORMAT_SSHORT = 8;
358 private static final int IFD_FORMAT_SLONG = 9;
359 private static final int IFD_FORMAT_SRATIONAL = 10;
360 private static final int IFD_FORMAT_SINGLE = 11;
361 private static final int IFD_FORMAT_DOUBLE = 12;
362 // Names for the data formats for debugging purpose.
363 private static final String[] IFD_FORMAT_NAMES = new String[] {
364 "", "BYTE", "STRING", "USHORT", "ULONG", "URATIONAL", "SBYTE", "UNDEFINED", "SSHORT",
365 "SLONG", "SRATIONAL", "SINGLE", "DOUBLE"
367 // Sizes of the components of each IFD value format
368 private static final int[] IFD_FORMAT_BYTES_PER_FORMAT = new int[] {
369 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8
371 private static final byte[] EXIF_ASCII_PREFIX = new byte[] {
372 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0
375 // A class for indicating EXIF rational type.
376 private static class Rational {
377 public final long numerator;
378 public final long denominator;
380 private Rational(long numerator, long denominator) {
381 // Handle erroneous case
382 if (denominator == 0) {
384 this.denominator = 1;
387 this.numerator = numerator;
388 this.denominator = denominator;
392 public String toString() {
393 return numerator + "/" + denominator;
396 public double calculate() {
397 return (double) numerator / denominator;
401 // A class for indicating EXIF attribute.
402 private static class ExifAttribute {
403 public final int format;
404 public final int numberOfComponents;
405 public final byte[] bytes;
407 private ExifAttribute(int format, int numberOfComponents, byte[] bytes) {
408 this.format = format;
409 this.numberOfComponents = numberOfComponents;
413 public static ExifAttribute createUShort(int[] values, ByteOrder byteOrder) {
414 final ByteBuffer buffer = ByteBuffer.wrap(
415 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_USHORT] * values.length]);
416 buffer.order(byteOrder);
417 for (int value : values) {
418 buffer.putShort((short) value);
420 return new ExifAttribute(IFD_FORMAT_USHORT, values.length, buffer.array());
423 public static ExifAttribute createUShort(int value, ByteOrder byteOrder) {
424 return createUShort(new int[] {value}, byteOrder);
427 public static ExifAttribute createULong(long[] values, ByteOrder byteOrder) {
428 final ByteBuffer buffer = ByteBuffer.wrap(
429 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_ULONG] * values.length]);
430 buffer.order(byteOrder);
431 for (long value : values) {
432 buffer.putInt((int) value);
434 return new ExifAttribute(IFD_FORMAT_ULONG, values.length, buffer.array());
437 public static ExifAttribute createULong(long value, ByteOrder byteOrder) {
438 return createULong(new long[] {value}, byteOrder);
441 public static ExifAttribute createSLong(int[] values, ByteOrder byteOrder) {
442 final ByteBuffer buffer = ByteBuffer.wrap(
443 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SLONG] * values.length]);
444 buffer.order(byteOrder);
445 for (int value : values) {
446 buffer.putInt(value);
448 return new ExifAttribute(IFD_FORMAT_SLONG, values.length, buffer.array());
451 public static ExifAttribute createSLong(int value, ByteOrder byteOrder) {
452 return createSLong(new int[] {value}, byteOrder);
455 public static ExifAttribute createByte(String value) {
456 // Exception for GPSAltitudeRef tag
457 if (value.length() == 1 && value.charAt(0) >= '0' && value.charAt(0) <= '1') {
458 final byte[] bytes = new byte[] { (byte) (value.charAt(0) - '0') };
459 return new ExifAttribute(IFD_FORMAT_BYTE, bytes.length, bytes);
461 final byte[] ascii = value.getBytes(ASCII);
462 return new ExifAttribute(IFD_FORMAT_BYTE, ascii.length, ascii);
465 public static ExifAttribute createString(String value) {
466 final byte[] ascii = (value + '\0').getBytes(ASCII);
467 return new ExifAttribute(IFD_FORMAT_STRING, ascii.length, ascii);
470 public static ExifAttribute createURational(Rational[] values, ByteOrder byteOrder) {
471 final ByteBuffer buffer = ByteBuffer.wrap(
472 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_URATIONAL] * values.length]);
473 buffer.order(byteOrder);
474 for (Rational value : values) {
475 buffer.putInt((int) value.numerator);
476 buffer.putInt((int) value.denominator);
478 return new ExifAttribute(IFD_FORMAT_URATIONAL, values.length, buffer.array());
481 public static ExifAttribute createURational(Rational value, ByteOrder byteOrder) {
482 return createURational(new Rational[] {value}, byteOrder);
485 public static ExifAttribute createSRational(Rational[] values, ByteOrder byteOrder) {
486 final ByteBuffer buffer = ByteBuffer.wrap(
487 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_SRATIONAL] * values.length]);
488 buffer.order(byteOrder);
489 for (Rational value : values) {
490 buffer.putInt((int) value.numerator);
491 buffer.putInt((int) value.denominator);
493 return new ExifAttribute(IFD_FORMAT_SRATIONAL, values.length, buffer.array());
496 public static ExifAttribute createSRational(Rational value, ByteOrder byteOrder) {
497 return createSRational(new Rational[] {value}, byteOrder);
500 public static ExifAttribute createDouble(double[] values, ByteOrder byteOrder) {
501 final ByteBuffer buffer = ByteBuffer.wrap(
502 new byte[IFD_FORMAT_BYTES_PER_FORMAT[IFD_FORMAT_DOUBLE] * values.length]);
503 buffer.order(byteOrder);
504 for (double value : values) {
505 buffer.putDouble(value);
507 return new ExifAttribute(IFD_FORMAT_DOUBLE, values.length, buffer.array());
510 public static ExifAttribute createDouble(double value, ByteOrder byteOrder) {
511 return createDouble(new double[] {value}, byteOrder);
515 public String toString() {
516 return "(" + IFD_FORMAT_NAMES[format] + ", data length:" + bytes.length + ")";
519 private Object getValue(ByteOrder byteOrder) {
521 ByteOrderAwarenessDataInputStream inputStream =
522 new ByteOrderAwarenessDataInputStream(bytes);
523 inputStream.setByteOrder(byteOrder);
525 case IFD_FORMAT_BYTE:
526 case IFD_FORMAT_SBYTE: {
527 // Exception for GPSAltitudeRef tag
528 if (bytes.length == 1 && bytes[0] >= 0 && bytes[0] <= 1) {
529 return new String(new char[] { (char) (bytes[0] + '0') });
531 return new String(bytes, ASCII);
533 case IFD_FORMAT_UNDEFINED:
534 case IFD_FORMAT_STRING: {
536 if (numberOfComponents >= EXIF_ASCII_PREFIX.length) {
538 for (int i = 0; i < EXIF_ASCII_PREFIX.length; ++i) {
539 if (bytes[i] != EXIF_ASCII_PREFIX[i]) {
545 index = EXIF_ASCII_PREFIX.length;
549 StringBuilder stringBuilder = new StringBuilder();
550 while (index < numberOfComponents) {
551 int ch = bytes[index];
556 stringBuilder.append((char) ch);
558 stringBuilder.append('?');
562 return stringBuilder.toString();
564 case IFD_FORMAT_USHORT: {
565 final int[] values = new int[numberOfComponents];
566 for (int i = 0; i < numberOfComponents; ++i) {
567 values[i] = inputStream.readUnsignedShort();
571 case IFD_FORMAT_ULONG: {
572 final long[] values = new long[numberOfComponents];
573 for (int i = 0; i < numberOfComponents; ++i) {
574 values[i] = inputStream.readUnsignedInt();
578 case IFD_FORMAT_URATIONAL: {
579 final Rational[] values = new Rational[numberOfComponents];
580 for (int i = 0; i < numberOfComponents; ++i) {
581 final long numerator = inputStream.readUnsignedInt();
582 final long denominator = inputStream.readUnsignedInt();
583 values[i] = new Rational(numerator, denominator);
587 case IFD_FORMAT_SSHORT: {
588 final int[] values = new int[numberOfComponents];
589 for (int i = 0; i < numberOfComponents; ++i) {
590 values[i] = inputStream.readShort();
594 case IFD_FORMAT_SLONG: {
595 final int[] values = new int[numberOfComponents];
596 for (int i = 0; i < numberOfComponents; ++i) {
597 values[i] = inputStream.readInt();
601 case IFD_FORMAT_SRATIONAL: {
602 final Rational[] values = new Rational[numberOfComponents];
603 for (int i = 0; i < numberOfComponents; ++i) {
604 final long numerator = inputStream.readInt();
605 final long denominator = inputStream.readInt();
606 values[i] = new Rational(numerator, denominator);
610 case IFD_FORMAT_SINGLE: {
611 final double[] values = new double[numberOfComponents];
612 for (int i = 0; i < numberOfComponents; ++i) {
613 values[i] = inputStream.readFloat();
617 case IFD_FORMAT_DOUBLE: {
618 final double[] values = new double[numberOfComponents];
619 for (int i = 0; i < numberOfComponents; ++i) {
620 values[i] = inputStream.readDouble();
627 } catch (IOException e) {
628 Log.w(TAG, "IOException occurred during reading a value", e);
633 public double getDoubleValue(ByteOrder byteOrder) {
634 Object value = getValue(byteOrder);
636 throw new NumberFormatException("NULL can't be converted to a double value");
638 if (value instanceof String) {
639 return Double.parseDouble((String) value);
641 if (value instanceof long[]) {
642 long[] array = (long[]) value;
643 if (array.length == 1) {
646 throw new NumberFormatException("There are more than one component");
648 if (value instanceof int[]) {
649 int[] array = (int[]) value;
650 if (array.length == 1) {
653 throw new NumberFormatException("There are more than one component");
655 if (value instanceof double[]) {
656 double[] array = (double[]) value;
657 if (array.length == 1) {
660 throw new NumberFormatException("There are more than one component");
662 if (value instanceof Rational[]) {
663 Rational[] array = (Rational[]) value;
664 if (array.length == 1) {
665 return array[0].calculate();
667 throw new NumberFormatException("There are more than one component");
669 throw new NumberFormatException("Couldn't find a double value");
672 public int getIntValue(ByteOrder byteOrder) {
673 Object value = getValue(byteOrder);
675 throw new NumberFormatException("NULL can't be converted to a integer value");
677 if (value instanceof String) {
678 return Integer.parseInt((String) value);
680 if (value instanceof long[]) {
681 long[] array = (long[]) value;
682 if (array.length == 1) {
683 return (int) array[0];
685 throw new NumberFormatException("There are more than one component");
687 if (value instanceof int[]) {
688 int[] array = (int[]) value;
689 if (array.length == 1) {
692 throw new NumberFormatException("There are more than one component");
694 throw new NumberFormatException("Couldn't find a integer value");
697 public String getStringValue(ByteOrder byteOrder) {
698 Object value = getValue(byteOrder);
702 if (value instanceof String) {
703 return (String) value;
706 final StringBuilder stringBuilder = new StringBuilder();
707 if (value instanceof long[]) {
708 long[] array = (long[]) value;
709 for (int i = 0; i < array.length; ++i) {
710 stringBuilder.append(array[i]);
711 if (i + 1 != array.length) {
712 stringBuilder.append(",");
715 return stringBuilder.toString();
717 if (value instanceof int[]) {
718 int[] array = (int[]) value;
719 for (int i = 0; i < array.length; ++i) {
720 stringBuilder.append(array[i]);
721 if (i + 1 != array.length) {
722 stringBuilder.append(",");
725 return stringBuilder.toString();
727 if (value instanceof double[]) {
728 double[] array = (double[]) value;
729 for (int i = 0; i < array.length; ++i) {
730 stringBuilder.append(array[i]);
731 if (i + 1 != array.length) {
732 stringBuilder.append(",");
735 return stringBuilder.toString();
737 if (value instanceof Rational[]) {
738 Rational[] array = (Rational[]) value;
739 for (int i = 0; i < array.length; ++i) {
740 stringBuilder.append(array[i].numerator);
741 stringBuilder.append('/');
742 stringBuilder.append(array[i].denominator);
743 if (i + 1 != array.length) {
744 stringBuilder.append(",");
747 return stringBuilder.toString();
753 return IFD_FORMAT_BYTES_PER_FORMAT[format] * numberOfComponents;
757 // A class for indicating EXIF tag.
758 private static class ExifTag {
759 public final int number;
760 public final String name;
761 public final int primaryFormat;
762 public final int secondaryFormat;
764 private ExifTag(String name, int number, int format) {
766 this.number = number;
767 this.primaryFormat = format;
768 this.secondaryFormat = -1;
771 private ExifTag(String name, int number, int primaryFormat, int secondaryFormat) {
773 this.number = number;
774 this.primaryFormat = primaryFormat;
775 this.secondaryFormat = secondaryFormat;
779 // Primary image IFD TIFF tags (See JEITA CP-3451 Table 14. page 54).
780 private static final ExifTag[] IFD_TIFF_TAGS = new ExifTag[] {
781 new ExifTag(TAG_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
782 new ExifTag(TAG_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
783 new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
784 new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
785 new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
786 new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
787 new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
788 new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
789 new ExifTag(TAG_STRIP_OFFSETS, 273, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
790 new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
791 new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
792 new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
793 new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
794 new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
795 new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
796 new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
797 new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
798 new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
799 new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
800 new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
801 new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
802 new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
803 new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
804 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
805 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
806 new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
807 new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
808 new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
809 new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
810 new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
811 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
812 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
815 // Primary image IFD Exif Private tags (See JEITA CP-3451 Table 15. page 55).
816 private static final ExifTag[] IFD_EXIF_TAGS = new ExifTag[] {
817 new ExifTag(TAG_EXPOSURE_TIME, 33434, IFD_FORMAT_URATIONAL),
818 new ExifTag(TAG_APERTURE, 33437, IFD_FORMAT_URATIONAL),
819 new ExifTag(TAG_EXPOSURE_PROGRAM, 34850, IFD_FORMAT_USHORT),
820 new ExifTag(TAG_SPECTRAL_SENSITIVITY, 34852, IFD_FORMAT_STRING),
821 new ExifTag(TAG_ISO, 34855, IFD_FORMAT_USHORT),
822 new ExifTag(TAG_OECF, 34856, IFD_FORMAT_UNDEFINED),
823 new ExifTag(TAG_EXIF_VERSION, 36864, IFD_FORMAT_STRING),
824 new ExifTag(TAG_DATETIME_ORIGINAL, 36867, IFD_FORMAT_STRING),
825 new ExifTag(TAG_DATETIME_DIGITIZED, 36868, IFD_FORMAT_STRING),
826 new ExifTag(TAG_COMPONENTS_CONFIGURATION, 37121, IFD_FORMAT_UNDEFINED),
827 new ExifTag(TAG_COMPRESSED_BITS_PER_PIXEL, 37122, IFD_FORMAT_URATIONAL),
828 new ExifTag(TAG_SHUTTER_SPEED_VALUE, 37377, IFD_FORMAT_SRATIONAL),
829 new ExifTag(TAG_APERTURE_VALUE, 37378, IFD_FORMAT_URATIONAL),
830 new ExifTag(TAG_BRIGHTNESS_VALUE, 37379, IFD_FORMAT_SRATIONAL),
831 new ExifTag(TAG_EXPOSURE_BIAS_VALUE, 37380, IFD_FORMAT_SRATIONAL),
832 new ExifTag(TAG_MAX_APERTURE_VALUE, 37381, IFD_FORMAT_URATIONAL),
833 new ExifTag(TAG_SUBJECT_DISTANCE, 37382, IFD_FORMAT_URATIONAL),
834 new ExifTag(TAG_METERING_MODE, 37383, IFD_FORMAT_USHORT),
835 new ExifTag(TAG_LIGHT_SOURCE, 37384, IFD_FORMAT_USHORT),
836 new ExifTag(TAG_FLASH, 37385, IFD_FORMAT_USHORT),
837 new ExifTag(TAG_FOCAL_LENGTH, 37386, IFD_FORMAT_URATIONAL),
838 new ExifTag(TAG_SUBJECT_AREA, 37396, IFD_FORMAT_USHORT),
839 new ExifTag(TAG_MAKER_NOTE, 37500, IFD_FORMAT_UNDEFINED),
840 new ExifTag(TAG_USER_COMMENT, 37510, IFD_FORMAT_UNDEFINED),
841 new ExifTag(TAG_SUBSEC_TIME, 37520, IFD_FORMAT_STRING),
842 new ExifTag(TAG_SUBSEC_TIME_ORIG, 37521, IFD_FORMAT_STRING),
843 new ExifTag(TAG_SUBSEC_TIME_DIG, 37522, IFD_FORMAT_STRING),
844 new ExifTag(TAG_FLASHPIX_VERSION, 40960, IFD_FORMAT_UNDEFINED),
845 new ExifTag(TAG_COLOR_SPACE, 40961, IFD_FORMAT_USHORT),
846 new ExifTag(TAG_PIXEL_X_DIMENSION, 40962, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
847 new ExifTag(TAG_PIXEL_Y_DIMENSION, 40963, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
848 new ExifTag(TAG_RELATED_SOUND_FILE, 40964, IFD_FORMAT_STRING),
849 new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
850 new ExifTag(TAG_FLASH_ENERGY, 41483, IFD_FORMAT_URATIONAL),
851 new ExifTag(TAG_SPATIAL_FREQUENCY_RESPONSE, 41484, IFD_FORMAT_UNDEFINED),
852 new ExifTag(TAG_FOCAL_PLANE_X_RESOLUTION, 41486, IFD_FORMAT_URATIONAL),
853 new ExifTag(TAG_FOCAL_PLANE_Y_RESOLUTION, 41487, IFD_FORMAT_URATIONAL),
854 new ExifTag(TAG_FOCAL_PLANE_RESOLUTION_UNIT, 41488, IFD_FORMAT_USHORT),
855 new ExifTag(TAG_SUBJECT_LOCATION, 41492, IFD_FORMAT_USHORT),
856 new ExifTag(TAG_EXPOSURE_INDEX, 41493, IFD_FORMAT_URATIONAL),
857 new ExifTag(TAG_SENSING_METHOD, 41495, IFD_FORMAT_USHORT),
858 new ExifTag(TAG_FILE_SOURCE, 41728, IFD_FORMAT_UNDEFINED),
859 new ExifTag(TAG_SCENE_TYPE, 41729, IFD_FORMAT_UNDEFINED),
860 new ExifTag(TAG_CFA_PATTERN, 41730, IFD_FORMAT_UNDEFINED),
861 new ExifTag(TAG_CUSTOM_RENDERED, 41985, IFD_FORMAT_USHORT),
862 new ExifTag(TAG_EXPOSURE_MODE, 41986, IFD_FORMAT_USHORT),
863 new ExifTag(TAG_WHITE_BALANCE, 41987, IFD_FORMAT_USHORT),
864 new ExifTag(TAG_DIGITAL_ZOOM_RATIO, 41988, IFD_FORMAT_URATIONAL),
865 new ExifTag(TAG_FOCAL_LENGTH_IN_35MM_FILM, 41989, IFD_FORMAT_USHORT),
866 new ExifTag(TAG_SCENE_CAPTURE_TYPE, 41990, IFD_FORMAT_USHORT),
867 new ExifTag(TAG_GAIN_CONTROL, 41991, IFD_FORMAT_USHORT),
868 new ExifTag(TAG_CONTRAST, 41992, IFD_FORMAT_USHORT),
869 new ExifTag(TAG_SATURATION, 41993, IFD_FORMAT_USHORT),
870 new ExifTag(TAG_SHARPNESS, 41994, IFD_FORMAT_USHORT),
871 new ExifTag(TAG_DEVICE_SETTING_DESCRIPTION, 41995, IFD_FORMAT_UNDEFINED),
872 new ExifTag(TAG_SUBJECT_DISTANCE_RANGE, 41996, IFD_FORMAT_USHORT),
873 new ExifTag(TAG_IMAGE_UNIQUE_ID, 42016, IFD_FORMAT_STRING),
876 // Primary image IFD GPS Info tags (See JEITA CP-3451 Table 16. page 56).
877 private static final ExifTag[] IFD_GPS_TAGS = new ExifTag[] {
878 new ExifTag(TAG_GPS_VERSION_ID, 0, IFD_FORMAT_BYTE),
879 new ExifTag(TAG_GPS_LATITUDE_REF, 1, IFD_FORMAT_STRING),
880 new ExifTag(TAG_GPS_LATITUDE, 2, IFD_FORMAT_URATIONAL),
881 new ExifTag(TAG_GPS_LONGITUDE_REF, 3, IFD_FORMAT_STRING),
882 new ExifTag(TAG_GPS_LONGITUDE, 4, IFD_FORMAT_URATIONAL),
883 new ExifTag(TAG_GPS_ALTITUDE_REF, 5, IFD_FORMAT_BYTE),
884 new ExifTag(TAG_GPS_ALTITUDE, 6, IFD_FORMAT_URATIONAL),
885 new ExifTag(TAG_GPS_TIMESTAMP, 7, IFD_FORMAT_URATIONAL),
886 new ExifTag(TAG_GPS_SATELLITES, 8, IFD_FORMAT_STRING),
887 new ExifTag(TAG_GPS_STATUS, 9, IFD_FORMAT_STRING),
888 new ExifTag(TAG_GPS_MEASURE_MODE, 10, IFD_FORMAT_STRING),
889 new ExifTag(TAG_GPS_DOP, 11, IFD_FORMAT_URATIONAL),
890 new ExifTag(TAG_GPS_SPEED_REF, 12, IFD_FORMAT_STRING),
891 new ExifTag(TAG_GPS_SPEED, 13, IFD_FORMAT_URATIONAL),
892 new ExifTag(TAG_GPS_TRACK_REF, 14, IFD_FORMAT_STRING),
893 new ExifTag(TAG_GPS_TRACK, 15, IFD_FORMAT_URATIONAL),
894 new ExifTag(TAG_GPS_IMG_DIRECTION_REF, 16, IFD_FORMAT_STRING),
895 new ExifTag(TAG_GPS_IMG_DIRECTION, 17, IFD_FORMAT_URATIONAL),
896 new ExifTag(TAG_GPS_MAP_DATUM, 18, IFD_FORMAT_STRING),
897 new ExifTag(TAG_GPS_DEST_LATITUDE_REF, 19, IFD_FORMAT_STRING),
898 new ExifTag(TAG_GPS_DEST_LATITUDE, 20, IFD_FORMAT_URATIONAL),
899 new ExifTag(TAG_GPS_DEST_LONGITUDE_REF, 21, IFD_FORMAT_STRING),
900 new ExifTag(TAG_GPS_DEST_LONGITUDE, 22, IFD_FORMAT_URATIONAL),
901 new ExifTag(TAG_GPS_DEST_BEARING_REF, 23, IFD_FORMAT_STRING),
902 new ExifTag(TAG_GPS_DEST_BEARING, 24, IFD_FORMAT_URATIONAL),
903 new ExifTag(TAG_GPS_DEST_DISTANCE_REF, 25, IFD_FORMAT_STRING),
904 new ExifTag(TAG_GPS_DEST_DISTANCE, 26, IFD_FORMAT_URATIONAL),
905 new ExifTag(TAG_GPS_PROCESSING_METHOD, 27, IFD_FORMAT_UNDEFINED),
906 new ExifTag(TAG_GPS_AREA_INFORMATION, 28, IFD_FORMAT_UNDEFINED),
907 new ExifTag(TAG_GPS_DATESTAMP, 29, IFD_FORMAT_STRING),
908 new ExifTag(TAG_GPS_DIFFERENTIAL, 30, IFD_FORMAT_USHORT),
910 // Primary image IFD Interoperability tag (See JEITA CP-3451 Table 17. page 56).
911 private static final ExifTag[] IFD_INTEROPERABILITY_TAGS = new ExifTag[] {
912 new ExifTag(TAG_INTEROPERABILITY_INDEX, 1, IFD_FORMAT_STRING),
914 // IFD Thumbnail tags (See JEITA CP-3451 Table 18. page 57).
915 private static final ExifTag[] IFD_THUMBNAIL_TAGS = new ExifTag[] {
916 new ExifTag(TAG_THUMBNAIL_IMAGE_WIDTH, 256, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
917 new ExifTag(TAG_THUMBNAIL_IMAGE_LENGTH, 257, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
918 new ExifTag(TAG_BITS_PER_SAMPLE, 258, IFD_FORMAT_USHORT),
919 new ExifTag(TAG_COMPRESSION, 259, IFD_FORMAT_USHORT),
920 new ExifTag(TAG_PHOTOMETRIC_INTERPRETATION, 262, IFD_FORMAT_USHORT),
921 new ExifTag(TAG_IMAGE_DESCRIPTION, 270, IFD_FORMAT_STRING),
922 new ExifTag(TAG_MAKE, 271, IFD_FORMAT_STRING),
923 new ExifTag(TAG_MODEL, 272, IFD_FORMAT_STRING),
924 new ExifTag(TAG_STRIP_OFFSETS, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
925 new ExifTag(TAG_ORIENTATION, 274, IFD_FORMAT_USHORT),
926 new ExifTag(TAG_SAMPLES_PER_PIXEL, 277, IFD_FORMAT_USHORT),
927 new ExifTag(TAG_ROWS_PER_STRIP, 278, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
928 new ExifTag(TAG_STRIP_BYTE_COUNTS, 279, IFD_FORMAT_USHORT, IFD_FORMAT_ULONG),
929 new ExifTag(TAG_X_RESOLUTION, 282, IFD_FORMAT_URATIONAL),
930 new ExifTag(TAG_Y_RESOLUTION, 283, IFD_FORMAT_URATIONAL),
931 new ExifTag(TAG_PLANAR_CONFIGURATION, 284, IFD_FORMAT_USHORT),
932 new ExifTag(TAG_RESOLUTION_UNIT, 296, IFD_FORMAT_USHORT),
933 new ExifTag(TAG_TRANSFER_FUNCTION, 301, IFD_FORMAT_USHORT),
934 new ExifTag(TAG_SOFTWARE, 305, IFD_FORMAT_STRING),
935 new ExifTag(TAG_DATETIME, 306, IFD_FORMAT_STRING),
936 new ExifTag(TAG_ARTIST, 315, IFD_FORMAT_STRING),
937 new ExifTag(TAG_WHITE_POINT, 318, IFD_FORMAT_URATIONAL),
938 new ExifTag(TAG_PRIMARY_CHROMATICITIES, 319, IFD_FORMAT_URATIONAL),
939 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG),
940 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG),
941 new ExifTag(TAG_Y_CB_CR_COEFFICIENTS, 529, IFD_FORMAT_URATIONAL),
942 new ExifTag(TAG_Y_CB_CR_SUB_SAMPLING, 530, IFD_FORMAT_USHORT),
943 new ExifTag(TAG_Y_CB_CR_POSITIONING, 531, IFD_FORMAT_USHORT),
944 new ExifTag(TAG_REFERENCE_BLACK_WHITE, 532, IFD_FORMAT_URATIONAL),
945 new ExifTag(TAG_COPYRIGHT, 33432, IFD_FORMAT_STRING),
946 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
947 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
950 // See JEITA CP-3451 Figure 5. page 9.
951 // The following values are used for indicating pointers to the other Image File Directorys.
953 // Indices of Exif Ifd tag groups
954 private static final int IFD_TIFF_HINT = 0;
955 private static final int IFD_EXIF_HINT = 1;
956 private static final int IFD_GPS_HINT = 2;
957 private static final int IFD_INTEROPERABILITY_HINT = 3;
958 private static final int IFD_THUMBNAIL_HINT = 4;
959 // List of Exif tag groups
960 private static final ExifTag[][] EXIF_TAGS = new ExifTag[][] {
961 IFD_TIFF_TAGS, IFD_EXIF_TAGS, IFD_GPS_TAGS, IFD_INTEROPERABILITY_TAGS,
964 // List of tags for pointing to the other image file directory offset.
965 private static final ExifTag[] IFD_POINTER_TAGS = new ExifTag[] {
966 new ExifTag(TAG_EXIF_IFD_POINTER, 34665, IFD_FORMAT_ULONG),
967 new ExifTag(TAG_GPS_INFO_IFD_POINTER, 34853, IFD_FORMAT_ULONG),
968 new ExifTag(TAG_INTEROPERABILITY_IFD_POINTER, 40965, IFD_FORMAT_ULONG),
970 // List of indices of the indicated tag groups according to the IFD_POINTER_TAGS
971 private static final int[] IFD_POINTER_TAG_HINTS = new int[] {
972 IFD_EXIF_HINT, IFD_GPS_HINT, IFD_INTEROPERABILITY_HINT
974 // Tags for indicating the thumbnail offset and length
975 private static final ExifTag JPEG_INTERCHANGE_FORMAT_TAG =
976 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT, 513, IFD_FORMAT_ULONG);
977 private static final ExifTag JPEG_INTERCHANGE_FORMAT_LENGTH_TAG =
978 new ExifTag(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, 514, IFD_FORMAT_ULONG);
980 // Mappings from tag number to tag name and each item represents one IFD tag group.
981 private static final HashMap[] sExifTagMapsForReading = new HashMap[EXIF_TAGS.length];
982 // Mappings from tag name to tag number and each item represents one IFD tag group.
983 private static final HashMap[] sExifTagMapsForWriting = new HashMap[EXIF_TAGS.length];
984 private static final HashSet<String> sTagSetForCompatibility = new HashSet<>(Arrays.asList(
985 TAG_APERTURE, TAG_DIGITAL_ZOOM_RATIO, TAG_EXPOSURE_TIME, TAG_SUBJECT_DISTANCE,
988 // See JPEG File Interchange Format Version 1.02.
989 // The following values are defined for handling JPEG streams. In this implementation, we are
990 // not only getting information from EXIF but also from some JPEG special segments such as
991 // MARKER_COM for user comment and MARKER_SOFx for image width and height.
993 private static final Charset ASCII = Charset.forName("US-ASCII");
994 // Identifier for EXIF APP1 segment in JPEG
995 private static final byte[] IDENTIFIER_EXIF_APP1 = "Exif\0\0".getBytes(ASCII);
996 // JPEG segment markers, that each marker consumes two bytes beginning with 0xff and ending with
997 // the indicator. There is no SOF4, SOF8, SOF16 markers in JPEG and SOFx markers indicates start
998 // of frame(baseline DCT) and the image size info exists in its beginning part.
999 private static final byte MARKER = (byte) 0xff;
1000 private static final byte MARKER_SOI = (byte) 0xd8;
1001 private static final byte MARKER_SOF0 = (byte) 0xc0;
1002 private static final byte MARKER_SOF1 = (byte) 0xc1;
1003 private static final byte MARKER_SOF2 = (byte) 0xc2;
1004 private static final byte MARKER_SOF3 = (byte) 0xc3;
1005 private static final byte MARKER_SOF5 = (byte) 0xc5;
1006 private static final byte MARKER_SOF6 = (byte) 0xc6;
1007 private static final byte MARKER_SOF7 = (byte) 0xc7;
1008 private static final byte MARKER_SOF9 = (byte) 0xc9;
1009 private static final byte MARKER_SOF10 = (byte) 0xca;
1010 private static final byte MARKER_SOF11 = (byte) 0xcb;
1011 private static final byte MARKER_SOF13 = (byte) 0xcd;
1012 private static final byte MARKER_SOF14 = (byte) 0xce;
1013 private static final byte MARKER_SOF15 = (byte) 0xcf;
1014 private static final byte MARKER_SOS = (byte) 0xda;
1015 private static final byte MARKER_APP1 = (byte) 0xe1;
1016 private static final byte MARKER_COM = (byte) 0xfe;
1017 private static final byte MARKER_EOI = (byte) 0xd9;
1020 sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
1021 sFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1023 // Build up the hash tables to look up Exif tags for reading Exif tags.
1024 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
1025 sExifTagMapsForReading[hint] = new HashMap();
1026 sExifTagMapsForWriting[hint] = new HashMap();
1027 for (ExifTag tag : EXIF_TAGS[hint]) {
1028 sExifTagMapsForReading[hint].put(tag.number, tag);
1029 sExifTagMapsForWriting[hint].put(tag.name, tag);
1034 private final String mFilename;
1035 private final HashMap[] mAttributes = new HashMap[EXIF_TAGS.length];
1036 private ByteOrder mExifByteOrder = ByteOrder.BIG_ENDIAN;
1037 private boolean mHasThumbnail;
1038 // The following values used for indicating a thumbnail position.
1039 private int mThumbnailOffset;
1040 private int mThumbnailLength;
1041 private byte[] mThumbnailBytes;
1042 private boolean mIsSupportedFile;
1044 // Pattern to check non zero timestamp
1045 private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
1046 // Pattern to check gps timestamp
1047 private static final Pattern sGpsTimestampPattern =
1048 Pattern.compile("^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$");
1051 * Reads Exif tags from the specified image file.
1053 public ExifInterface(String filename) throws IOException {
1054 if (filename == null) {
1055 throw new IllegalArgumentException("filename cannot be null");
1057 mFilename = filename;
1063 * Returns the EXIF attribute of the specified tag or {@code null} if there is no such tag in
1066 * @param tag the name of the tag.
1068 private ExifAttribute getExifAttribute(String tag) {
1069 // Retrieves all tag groups. The value from primary image tag group has a higher priority
1070 // than the value from the thumbnail tag group if there are more than one candidates.
1071 for (int i = 0; i < EXIF_TAGS.length; ++i) {
1072 Object value = mAttributes[i].get(tag);
1073 if (value != null) {
1074 return (ExifAttribute) value;
1081 * Returns the value of the specified tag or {@code null} if there
1082 * is no such tag in the image file.
1084 * @param tag the name of the tag.
1086 public String getAttribute(String tag) {
1087 ExifAttribute attribute = getExifAttribute(tag);
1088 if (attribute != null) {
1089 if (!sTagSetForCompatibility.contains(tag)) {
1090 return attribute.getStringValue(mExifByteOrder);
1092 if (tag.equals(TAG_GPS_TIMESTAMP)) {
1093 // Convert the rational values to the custom formats for backwards compatibility.
1094 if (attribute.format != IFD_FORMAT_URATIONAL
1095 && attribute.format != IFD_FORMAT_SRATIONAL) {
1098 Rational[] array = (Rational[]) attribute.getValue(mExifByteOrder);
1099 if (array.length != 3) {
1102 return String.format("%02d:%02d:%02d",
1103 (int) ((float) array[0].numerator / array[0].denominator),
1104 (int) ((float) array[1].numerator / array[1].denominator),
1105 (int) ((float) array[2].numerator / array[2].denominator));
1108 return Double.toString(attribute.getDoubleValue(mExifByteOrder));
1109 } catch (NumberFormatException e) {
1117 * Returns the integer value of the specified tag. If there is no such tag
1118 * in the image file or the value cannot be parsed as integer, return
1119 * <var>defaultValue</var>.
1121 * @param tag the name of the tag.
1122 * @param defaultValue the value to return if the tag is not available.
1124 public int getAttributeInt(String tag, int defaultValue) {
1125 ExifAttribute exifAttribute = getExifAttribute(tag);
1126 if (exifAttribute == null) {
1127 return defaultValue;
1131 return exifAttribute.getIntValue(mExifByteOrder);
1132 } catch (NumberFormatException e) {
1133 return defaultValue;
1138 * Returns the double value of the tag that is specified as rational or contains a
1139 * double-formatted value. If there is no such tag in the image file or the value cannot be
1140 * parsed as double, return <var>defaultValue</var>.
1142 * @param tag the name of the tag.
1143 * @param defaultValue the value to return if the tag is not available.
1145 public double getAttributeDouble(String tag, double defaultValue) {
1146 ExifAttribute exifAttribute = getExifAttribute(tag);
1147 if (exifAttribute == null) {
1148 return defaultValue;
1152 return exifAttribute.getDoubleValue(mExifByteOrder);
1153 } catch (NumberFormatException e) {
1154 return defaultValue;
1159 * Set the value of the specified tag.
1161 * @param tag the name of the tag.
1162 * @param value the value of the tag.
1164 public void setAttribute(String tag, String value) {
1165 // Convert the given value to rational values for backwards compatibility.
1166 if (value != null && sTagSetForCompatibility.contains(tag)) {
1167 if (tag.equals(TAG_GPS_TIMESTAMP)) {
1168 Matcher m = sGpsTimestampPattern.matcher(value);
1170 Log.w(TAG, "Invalid value for " + tag + " : " + value);
1173 value = Integer.parseInt(m.group(1)) + "/1," + Integer.parseInt(m.group(2)) + "/1,"
1174 + Integer.parseInt(m.group(3)) + "/1";
1177 double doubleValue = Double.parseDouble(value);
1178 value = (long) (doubleValue * 10000L) + "/10000";
1179 } catch (NumberFormatException e) {
1180 Log.w(TAG, "Invalid value for " + tag + " : " + value);
1186 for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1187 if (i == IFD_THUMBNAIL_HINT && !mHasThumbnail) {
1190 final Object obj = sExifTagMapsForWriting[i].get(tag);
1192 if (value == null) {
1193 mAttributes[i].remove(tag);
1196 final ExifTag exifTag = (ExifTag) obj;
1197 Pair<Integer, Integer> guess = guessDataFormat(value);
1199 if (exifTag.primaryFormat == guess.first || exifTag.primaryFormat == guess.second) {
1200 dataFormat = exifTag.primaryFormat;
1201 } else if (exifTag.secondaryFormat != -1 && (exifTag.secondaryFormat == guess.first
1202 || exifTag.secondaryFormat == guess.second)) {
1203 dataFormat = exifTag.secondaryFormat;
1204 } else if (exifTag.primaryFormat == IFD_FORMAT_BYTE
1205 || exifTag.primaryFormat == IFD_FORMAT_UNDEFINED
1206 || exifTag.primaryFormat == IFD_FORMAT_STRING) {
1207 dataFormat = exifTag.primaryFormat;
1209 Log.w(TAG, "Given tag (" + tag + ") value didn't match with one of expected "
1210 + "formats: " + IFD_FORMAT_NAMES[exifTag.primaryFormat]
1211 + (exifTag.secondaryFormat == -1 ? "" : ", "
1212 + IFD_FORMAT_NAMES[exifTag.secondaryFormat]) + " (guess: "
1213 + IFD_FORMAT_NAMES[guess.first] + (guess.second == -1 ? "" : ", "
1214 + IFD_FORMAT_NAMES[guess.second]) + ")");
1217 switch (dataFormat) {
1218 case IFD_FORMAT_BYTE: {
1219 mAttributes[i].put(tag, ExifAttribute.createByte(value));
1222 case IFD_FORMAT_UNDEFINED:
1223 case IFD_FORMAT_STRING: {
1224 mAttributes[i].put(tag, ExifAttribute.createString(value));
1227 case IFD_FORMAT_USHORT: {
1228 final String[] values = value.split(",");
1229 final int[] intArray = new int[values.length];
1230 for (int j = 0; j < values.length; ++j) {
1231 intArray[j] = Integer.parseInt(values[j]);
1233 mAttributes[i].put(tag,
1234 ExifAttribute.createUShort(intArray, mExifByteOrder));
1237 case IFD_FORMAT_SLONG: {
1238 final String[] values = value.split(",");
1239 final int[] intArray = new int[values.length];
1240 for (int j = 0; j < values.length; ++j) {
1241 intArray[j] = Integer.parseInt(values[j]);
1243 mAttributes[i].put(tag,
1244 ExifAttribute.createSLong(intArray, mExifByteOrder));
1247 case IFD_FORMAT_ULONG: {
1248 final String[] values = value.split(",");
1249 final long[] longArray = new long[values.length];
1250 for (int j = 0; j < values.length; ++j) {
1251 longArray[j] = Long.parseLong(values[j]);
1253 mAttributes[i].put(tag,
1254 ExifAttribute.createULong(longArray, mExifByteOrder));
1257 case IFD_FORMAT_URATIONAL: {
1258 final String[] values = value.split(",");
1259 final Rational[] rationalArray = new Rational[values.length];
1260 for (int j = 0; j < values.length; ++j) {
1261 final String[] numbers = values[j].split("/");
1262 rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
1263 Long.parseLong(numbers[1]));
1265 mAttributes[i].put(tag,
1266 ExifAttribute.createURational(rationalArray, mExifByteOrder));
1269 case IFD_FORMAT_SRATIONAL: {
1270 final String[] values = value.split(",");
1271 final Rational[] rationalArray = new Rational[values.length];
1272 for (int j = 0; j < values.length; ++j) {
1273 final String[] numbers = values[j].split("/");
1274 rationalArray[j] = new Rational(Long.parseLong(numbers[0]),
1275 Long.parseLong(numbers[1]));
1277 mAttributes[i].put(tag,
1278 ExifAttribute.createSRational(rationalArray, mExifByteOrder));
1281 case IFD_FORMAT_DOUBLE: {
1282 final String[] values = value.split(",");
1283 final double[] doubleArray = new double[values.length];
1284 for (int j = 0; j < values.length; ++j) {
1285 doubleArray[j] = Double.parseDouble(values[j]);
1287 mAttributes[i].put(tag,
1288 ExifAttribute.createDouble(doubleArray, mExifByteOrder));
1292 Log.w(TAG, "Data format isn't one of expected formats: " + dataFormat);
1300 * Update the values of the tags in the tag groups if any value for the tag already was stored.
1302 * @param tag the name of the tag.
1303 * @param value the value of the tag in a form of {@link ExifAttribute}.
1304 * @return Returns {@code true} if updating is placed.
1306 private boolean updateAttribute(String tag, ExifAttribute value) {
1307 boolean updated = false;
1308 for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1309 if (mAttributes[i].containsKey(tag)) {
1310 mAttributes[i].put(tag, value);
1318 * Remove any values of the specified tag.
1320 * @param tag the name of the tag.
1322 private void removeAttribute(String tag) {
1323 for (int i = 0 ; i < EXIF_TAGS.length; ++i) {
1324 mAttributes[i].remove(tag);
1329 * This function decides which parser to read the image data according to the given input stream
1330 * type and the content of the input stream. In each case, it reads the first three bytes to
1331 * determine whether the image data format is JPEG or not.
1333 private void loadAttributes() throws IOException {
1334 // Initialize mAttributes.
1335 for (int i = 0; i < EXIF_TAGS.length; ++i) {
1336 mAttributes[i] = new HashMap();
1339 InputStream in = new FileInputStream(mFilename);
1340 getJpegAttributes(in);
1341 mIsSupportedFile = true;
1342 } catch (IOException e) {
1343 // Ignore exceptions in order to keep the compatibility with the old versions of
1345 mIsSupportedFile = false;
1346 Log.w(TAG, "Invalid image.", e);
1348 addDefaultValuesForCompatibility();
1355 // Prints out attributes for debugging.
1356 private void printAttributes() {
1357 for (int i = 0; i < mAttributes.length; ++i) {
1358 Log.d(TAG, "The size of tag group[" + i + "]: " + mAttributes[i].size());
1359 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
1360 final ExifAttribute tagValue = (ExifAttribute) entry.getValue();
1361 Log.d(TAG, "tagName: " + entry.getKey() + ", tagType: " + tagValue.toString()
1362 + ", tagValue: '" + tagValue.getStringValue(mExifByteOrder) + "'");
1368 * Save the tag data into the original image file. This is expensive because it involves
1369 * copying all the data from one file to another and deleting the old file and renaming the
1370 * other. It's best to use {@link #setAttribute(String,String)} to set all attributes to write
1371 * and make a single call rather than multiple calls for each attribute.
1373 public void saveAttributes() throws IOException {
1374 if (!mIsSupportedFile) {
1375 throw new UnsupportedOperationException(
1376 "ExifInterface only supports saving attributes on JPEG formats.");
1378 // Keep the thumbnail in memory
1379 mThumbnailBytes = getThumbnail();
1381 File tempFile = null;
1382 // Move the original file to temporary file.
1383 tempFile = new File(mFilename + ".tmp");
1384 File originalFile = new File(mFilename);
1385 if (!originalFile.renameTo(tempFile)) {
1386 throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
1389 FileInputStream in = null;
1390 FileOutputStream out = null;
1392 // Save the new file.
1393 in = new FileInputStream(tempFile);
1394 out = new FileOutputStream(mFilename);
1395 saveJpegAttributes(in, out);
1397 IoUtils.closeQuietly(in);
1398 IoUtils.closeQuietly(out);
1402 // Discard the thumbnail in memory
1403 mThumbnailBytes = null;
1407 * Returns true if the image file has a thumbnail.
1409 public boolean hasThumbnail() {
1410 return mHasThumbnail;
1414 * Returns the thumbnail inside the image file, or {@code null} if there is no thumbnail.
1415 * The returned data is in JPEG format and can be decoded using
1416 * {@link android.graphics.BitmapFactory#decodeByteArray(byte[],int,int)}
1418 public byte[] getThumbnail() {
1419 if (!mHasThumbnail) {
1422 if (mThumbnailBytes != null) {
1423 return mThumbnailBytes;
1426 // Read the thumbnail.
1427 FileInputStream in = null;
1429 in = new FileInputStream(mFilename);
1430 if (in.skip(mThumbnailOffset) != mThumbnailOffset) {
1431 throw new IOException("Corrupted image");
1433 byte[] buffer = new byte[mThumbnailLength];
1434 if (in.read(buffer) != mThumbnailLength) {
1435 throw new IOException("Corrupted image");
1438 } catch (IOException e) {
1439 // Couldn't get a thumbnail image.
1441 IoUtils.closeQuietly(in);
1447 * Returns the offset and length of thumbnail inside the image file, or
1448 * {@code null} if there is no thumbnail.
1450 * @return two-element array, the offset in the first value, and length in
1451 * the second, or {@code null} if no thumbnail was found.
1454 public long[] getThumbnailRange() {
1455 if (!mHasThumbnail) {
1459 long[] range = new long[2];
1460 range[0] = mThumbnailOffset;
1461 range[1] = mThumbnailLength;
1467 * Stores the latitude and longitude value in a float array. The first element is
1468 * the latitude, and the second element is the longitude. Returns false if the
1469 * Exif tags are not available.
1471 public boolean getLatLong(float output[]) {
1472 String latValue = getAttribute(TAG_GPS_LATITUDE);
1473 String latRef = getAttribute(TAG_GPS_LATITUDE_REF);
1474 String lngValue = getAttribute(TAG_GPS_LONGITUDE);
1475 String lngRef = getAttribute(TAG_GPS_LONGITUDE_REF);
1477 if (latValue != null && latRef != null && lngValue != null && lngRef != null) {
1479 output[0] = convertRationalLatLonToFloat(latValue, latRef);
1480 output[1] = convertRationalLatLonToFloat(lngValue, lngRef);
1482 } catch (IllegalArgumentException e) {
1483 // if values are not parseable
1491 * Return the altitude in meters. If the exif tag does not exist, return
1492 * <var>defaultValue</var>.
1494 * @param defaultValue the value to return if the tag is not available.
1496 public double getAltitude(double defaultValue) {
1497 double altitude = getAttributeDouble(TAG_GPS_ALTITUDE, -1);
1498 int ref = getAttributeInt(TAG_GPS_ALTITUDE_REF, -1);
1500 if (altitude >= 0 && ref >= 0) {
1501 return (altitude * ((ref == 1) ? -1 : 1));
1503 return defaultValue;
1508 * Returns number of milliseconds since Jan. 1, 1970, midnight local time.
1509 * Returns -1 if the date time information if not available.
1512 public long getDateTime() {
1513 String dateTimeString = getAttribute(TAG_DATETIME);
1514 if (dateTimeString == null
1515 || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
1517 ParsePosition pos = new ParsePosition(0);
1519 // The exif field is in local time. Parsing it as if it is UTC will yield time
1520 // since 1/1/1970 local time
1521 Date datetime = sFormatter.parse(dateTimeString, pos);
1522 if (datetime == null) return -1;
1523 long msecs = datetime.getTime();
1525 String subSecs = getAttribute(TAG_SUBSEC_TIME);
1526 if (subSecs != null) {
1528 long sub = Long.valueOf(subSecs);
1529 while (sub > 1000) {
1533 } catch (NumberFormatException e) {
1538 } catch (IllegalArgumentException e) {
1544 * Returns number of milliseconds since Jan. 1, 1970, midnight UTC.
1545 * Returns -1 if the date time information if not available.
1548 public long getGpsDateTime() {
1549 String date = getAttribute(TAG_GPS_DATESTAMP);
1550 String time = getAttribute(TAG_GPS_TIMESTAMP);
1551 if (date == null || time == null
1552 || (!sNonZeroTimePattern.matcher(date).matches()
1553 && !sNonZeroTimePattern.matcher(time).matches())) {
1557 String dateTimeString = date + ' ' + time;
1559 ParsePosition pos = new ParsePosition(0);
1561 Date datetime = sFormatter.parse(dateTimeString, pos);
1562 if (datetime == null) return -1;
1563 return datetime.getTime();
1564 } catch (IllegalArgumentException e) {
1569 private static float convertRationalLatLonToFloat(String rationalString, String ref) {
1571 String [] parts = rationalString.split(",");
1574 pair = parts[0].split("/");
1575 double degrees = Double.parseDouble(pair[0].trim())
1576 / Double.parseDouble(pair[1].trim());
1578 pair = parts[1].split("/");
1579 double minutes = Double.parseDouble(pair[0].trim())
1580 / Double.parseDouble(pair[1].trim());
1582 pair = parts[2].split("/");
1583 double seconds = Double.parseDouble(pair[0].trim())
1584 / Double.parseDouble(pair[1].trim());
1586 double result = degrees + (minutes / 60.0) + (seconds / 3600.0);
1587 if ((ref.equals("S") || ref.equals("W"))) {
1588 return (float) -result;
1590 return (float) result;
1591 } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
1593 throw new IllegalArgumentException();
1597 // Loads EXIF attributes from a JPEG input stream.
1598 private void getJpegAttributes(InputStream inputStream) throws IOException {
1599 // See JPEG File Interchange Format Specification page 5.
1601 Log.d(TAG, "getJpegAttributes starting with: " + inputStream);
1603 DataInputStream dataInputStream = new DataInputStream(inputStream);
1606 if ((marker = dataInputStream.readByte()) != MARKER) {
1607 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1610 if (dataInputStream.readByte() != MARKER_SOI) {
1611 throw new IOException("Invalid marker: " + Integer.toHexString(marker & 0xff));
1615 marker = dataInputStream.readByte();
1616 if (marker != MARKER) {
1617 throw new IOException("Invalid marker:" + Integer.toHexString(marker & 0xff));
1620 marker = dataInputStream.readByte();
1622 Log.d(TAG, "Found JPEG segment indicator: " + Integer.toHexString(marker & 0xff));
1626 // EOI indicates the end of an image and in case of SOS, JPEG image stream starts and
1627 // the image data will terminate right after.
1628 if (marker == MARKER_EOI || marker == MARKER_SOS) {
1631 int length = dataInputStream.readUnsignedShort() - 2;
1634 Log.d(TAG, "JPEG segment: " + Integer.toHexString(marker & 0xff) + " (length: "
1635 + (length + 2) + ")");
1638 throw new IOException("Invalid length");
1643 Log.d(TAG, "MARKER_APP1");
1646 // Skip if it's not an EXIF APP1 segment.
1649 byte[] identifier = new byte[6];
1650 if (inputStream.read(identifier) != 6) {
1651 throw new IOException("Invalid exif");
1655 if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1656 // Skip if it's not an EXIF APP1 segment.
1660 throw new IOException("Invalid exif");
1663 Log.d(TAG, "readExifSegment with a byte array (length: " + length + ")");
1665 byte[] bytes = new byte[length];
1666 if (dataInputStream.read(bytes) != length) {
1667 throw new IOException("Invalid exif");
1669 readExifSegment(bytes, bytesRead);
1670 bytesRead += length;
1676 byte[] bytes = new byte[length];
1677 if (dataInputStream.read(bytes) != length) {
1678 throw new IOException("Invalid exif");
1681 if (getAttribute(TAG_USER_COMMENT) == null) {
1682 mAttributes[IFD_EXIF_HINT].put(TAG_USER_COMMENT, ExifAttribute.createString(
1683 new String(bytes, ASCII)));
1700 case MARKER_SOF15: {
1701 if (dataInputStream.skipBytes(1) != 1) {
1702 throw new IOException("Invalid SOFx");
1704 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH, ExifAttribute.createULong(
1705 dataInputStream.readUnsignedShort(), mExifByteOrder));
1706 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH, ExifAttribute.createULong(
1707 dataInputStream.readUnsignedShort(), mExifByteOrder));
1717 throw new IOException("Invalid length");
1719 if (dataInputStream.skipBytes(length) != length) {
1720 throw new IOException("Invalid JPEG segment");
1722 bytesRead += length;
1726 // Stores a new JPEG image with EXIF attributes into a given output stream.
1727 private void saveJpegAttributes(InputStream inputStream, OutputStream outputStream)
1728 throws IOException {
1729 // See JPEG File Interchange Format Specification page 5.
1731 Log.d(TAG, "saveJpegAttributes starting with (inputStream: " + inputStream
1732 + ", outputStream: " + outputStream + ")");
1734 DataInputStream dataInputStream = new DataInputStream(inputStream);
1735 ByteOrderAwarenessDataOutputStream dataOutputStream =
1736 new ByteOrderAwarenessDataOutputStream(outputStream, ByteOrder.BIG_ENDIAN);
1737 if (dataInputStream.readByte() != MARKER) {
1738 throw new IOException("Invalid marker");
1740 dataOutputStream.writeByte(MARKER);
1741 if (dataInputStream.readByte() != MARKER_SOI) {
1742 throw new IOException("Invalid marker");
1744 dataOutputStream.writeByte(MARKER_SOI);
1746 // Write EXIF APP1 segment
1747 dataOutputStream.writeByte(MARKER);
1748 dataOutputStream.writeByte(MARKER_APP1);
1749 writeExifSegment(dataOutputStream, 6);
1751 byte[] bytes = new byte[4096];
1754 byte marker = dataInputStream.readByte();
1755 if (marker != MARKER) {
1756 throw new IOException("Invalid marker");
1758 marker = dataInputStream.readByte();
1761 int length = dataInputStream.readUnsignedShort() - 2;
1763 throw new IOException("Invalid length");
1765 byte[] identifier = new byte[6];
1767 if (dataInputStream.read(identifier) != 6) {
1768 throw new IOException("Invalid exif");
1770 if (Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
1771 // Skip the original EXIF APP1 segment.
1772 if (dataInputStream.skip(length - 6) != length - 6) {
1773 throw new IOException("Invalid length");
1778 // Copy non-EXIF APP1 segment.
1779 dataOutputStream.writeByte(MARKER);
1780 dataOutputStream.writeByte(marker);
1781 dataOutputStream.writeUnsignedShort(length + 2);
1784 dataOutputStream.write(identifier);
1787 while (length > 0 && (read = dataInputStream.read(
1788 bytes, 0, Math.min(length, bytes.length))) >= 0) {
1789 dataOutputStream.write(bytes, 0, read);
1796 dataOutputStream.writeByte(MARKER);
1797 dataOutputStream.writeByte(marker);
1798 // Copy all the remaining data
1799 Streams.copy(dataInputStream, dataOutputStream);
1803 // Copy JPEG segment
1804 dataOutputStream.writeByte(MARKER);
1805 dataOutputStream.writeByte(marker);
1806 int length = dataInputStream.readUnsignedShort();
1807 dataOutputStream.writeUnsignedShort(length);
1810 throw new IOException("Invalid length");
1813 while (length > 0 && (read = dataInputStream.read(
1814 bytes, 0, Math.min(length, bytes.length))) >= 0) {
1815 dataOutputStream.write(bytes, 0, read);
1824 // Reads the given EXIF byte area and save its tag data into attributes.
1825 private void readExifSegment(byte[] exifBytes, int exifOffsetFromBeginning) throws IOException {
1826 // Parse TIFF Headers. See JEITA CP-3451C Table 1. page 10.
1827 ByteOrderAwarenessDataInputStream dataInputStream =
1828 new ByteOrderAwarenessDataInputStream(exifBytes);
1831 short byteOrder = dataInputStream.readShort();
1832 switch (byteOrder) {
1835 Log.d(TAG, "readExifSegment: Byte Align II");
1837 mExifByteOrder = ByteOrder.LITTLE_ENDIAN;
1841 Log.d(TAG, "readExifSegment: Byte Align MM");
1843 mExifByteOrder = ByteOrder.BIG_ENDIAN;
1846 throw new IOException("Invalid byte order: " + Integer.toHexString(byteOrder));
1850 dataInputStream.setByteOrder(mExifByteOrder);
1852 int startCode = dataInputStream.readUnsignedShort();
1853 if (startCode != 0x2a) {
1854 throw new IOException("Invalid exif start: " + Integer.toHexString(startCode));
1857 // Read first ifd offset
1858 long firstIfdOffset = dataInputStream.readUnsignedInt();
1859 if (firstIfdOffset < 8 || firstIfdOffset >= exifBytes.length) {
1860 throw new IOException("Invalid first Ifd offset: " + firstIfdOffset);
1862 firstIfdOffset -= 8;
1863 if (firstIfdOffset > 0) {
1864 if (dataInputStream.skip(firstIfdOffset) != firstIfdOffset) {
1865 throw new IOException("Couldn't jump to first Ifd: " + firstIfdOffset);
1869 // Read primary image TIFF image file directory.
1870 readImageFileDirectory(dataInputStream, IFD_TIFF_HINT);
1872 // Process thumbnail.
1873 String jpegInterchangeFormatString = getAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
1874 String jpegInterchangeFormatLengthString =
1875 getAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
1876 if (jpegInterchangeFormatString != null && jpegInterchangeFormatLengthString != null) {
1878 int jpegInterchangeFormat = Integer.parseInt(jpegInterchangeFormatString);
1879 int jpegInterchangeFormatLength = Integer
1880 .parseInt(jpegInterchangeFormatLengthString);
1881 // The following code limits the size of thumbnail size not to overflow EXIF data area.
1882 jpegInterchangeFormatLength = Math.min(jpegInterchangeFormat
1883 + jpegInterchangeFormatLength, exifBytes.length) - jpegInterchangeFormat;
1884 if (jpegInterchangeFormat > 0 && jpegInterchangeFormatLength > 0) {
1885 mHasThumbnail = true;
1886 mThumbnailOffset = exifOffsetFromBeginning + jpegInterchangeFormat;
1887 mThumbnailLength = jpegInterchangeFormatLength;
1889 } catch (NumberFormatException e) {
1890 // Ignored the corrupted image.
1895 private void addDefaultValuesForCompatibility() {
1896 // The value of DATETIME tag has the same value of DATETIME_ORIGINAL tag.
1897 String valueOfDateTimeOriginal = getAttribute(TAG_DATETIME_ORIGINAL);
1898 if (valueOfDateTimeOriginal != null) {
1899 mAttributes[IFD_TIFF_HINT].put(TAG_DATETIME,
1900 ExifAttribute.createString(valueOfDateTimeOriginal));
1903 // Add the default value.
1904 if (getAttribute(TAG_IMAGE_WIDTH) == null) {
1905 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_WIDTH,
1906 ExifAttribute.createULong(0, mExifByteOrder));
1908 if (getAttribute(TAG_IMAGE_LENGTH) == null) {
1909 mAttributes[IFD_TIFF_HINT].put(TAG_IMAGE_LENGTH,
1910 ExifAttribute.createULong(0, mExifByteOrder));
1912 if (getAttribute(TAG_ORIENTATION) == null) {
1913 mAttributes[IFD_TIFF_HINT].put(TAG_ORIENTATION,
1914 ExifAttribute.createULong(0, mExifByteOrder));
1916 if (getAttribute(TAG_LIGHT_SOURCE) == null) {
1917 mAttributes[IFD_EXIF_HINT].put(TAG_LIGHT_SOURCE,
1918 ExifAttribute.createULong(0, mExifByteOrder));
1922 // Reads image file directory, which is a tag group in EXIF.
1923 private void readImageFileDirectory(ByteOrderAwarenessDataInputStream dataInputStream, int hint)
1924 throws IOException {
1925 if (dataInputStream.peek() + 2 > dataInputStream.mLength) {
1926 // Return if there is no data from the offset.
1929 // See JEITA CP-3451 Figure 5. page 9.
1930 short numberOfDirectoryEntry = dataInputStream.readShort();
1931 if (dataInputStream.peek() + 12 * numberOfDirectoryEntry > dataInputStream.mLength) {
1932 // Return if the size of entries is too big.
1937 Log.d(TAG, "numberOfDirectoryEntry: " + numberOfDirectoryEntry);
1940 for (short i = 0; i < numberOfDirectoryEntry; ++i) {
1941 int tagNumber = dataInputStream.readUnsignedShort();
1942 int dataFormat = dataInputStream.readUnsignedShort();
1943 int numberOfComponents = dataInputStream.readInt();
1944 long nextEntryOffset = dataInputStream.peek() + 4; // next four bytes is for data
1946 // Look up a corresponding tag from tag number
1947 final ExifTag tag = (ExifTag) sExifTagMapsForReading[hint].get(tagNumber);
1950 Log.d(TAG, String.format("hint: %d, tagNumber: %d, tagName: %s, dataFormat: %d, " +
1951 "numberOfComponents: %d", hint, tagNumber, tag != null ? tag.name : null,
1952 dataFormat, numberOfComponents));
1955 if (tag == null || dataFormat <= 0 ||
1956 dataFormat >= IFD_FORMAT_BYTES_PER_FORMAT.length) {
1957 // Skip if the parsed tag number is not defined or invalid data format.
1959 Log.w(TAG, "Skip the tag entry since tag number is not defined: " + tagNumber);
1961 Log.w(TAG, "Skip the tag entry since data format is invalid: " + dataFormat);
1963 dataInputStream.seek(nextEntryOffset);
1967 // Read a value from data field or seek to the value offset which is stored in data
1968 // field if the size of the entry value is bigger than 4.
1969 int byteCount = numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat];
1970 if (byteCount > 4) {
1971 long offset = dataInputStream.readUnsignedInt();
1973 Log.d(TAG, "seek to data offset: " + offset);
1975 if (offset + byteCount <= dataInputStream.mLength) {
1976 dataInputStream.seek(offset);
1978 // Skip if invalid data offset.
1979 Log.w(TAG, "Skip the tag entry since data offset is invalid: " + offset);
1980 dataInputStream.seek(nextEntryOffset);
1985 // Recursively parse IFD when a IFD pointer tag appears.
1986 int innerIfdHint = getIfdHintFromTagNumber(tagNumber);
1988 Log.d(TAG, "innerIfdHint: " + innerIfdHint + " byteCount: " + byteCount);
1991 if (innerIfdHint >= 0) {
1993 // Get offset from data field
1994 switch (dataFormat) {
1995 case IFD_FORMAT_USHORT: {
1996 offset = dataInputStream.readUnsignedShort();
1999 case IFD_FORMAT_SSHORT: {
2000 offset = dataInputStream.readShort();
2003 case IFD_FORMAT_ULONG: {
2004 offset = dataInputStream.readUnsignedInt();
2007 case IFD_FORMAT_SLONG: {
2008 offset = dataInputStream.readInt();
2017 Log.d(TAG, String.format("Offset: %d, tagName: %s", offset, tag.name));
2019 if (offset > 0L && offset < dataInputStream.mLength) {
2020 dataInputStream.seek(offset);
2021 readImageFileDirectory(dataInputStream, innerIfdHint);
2023 Log.w(TAG, "Skip jump into the IFD since its offset is invalid: " + offset);
2026 dataInputStream.seek(nextEntryOffset);
2030 byte[] bytes = new byte[numberOfComponents * IFD_FORMAT_BYTES_PER_FORMAT[dataFormat]];
2031 dataInputStream.readFully(bytes);
2032 mAttributes[hint].put(
2033 tag.name, new ExifAttribute(dataFormat, numberOfComponents, bytes));
2034 if (dataInputStream.peek() != nextEntryOffset) {
2035 dataInputStream.seek(nextEntryOffset);
2039 if (dataInputStream.peek() + 4 <= dataInputStream.mLength) {
2040 long nextIfdOffset = dataInputStream.readUnsignedInt();
2042 Log.d(TAG, String.format("nextIfdOffset: %d", nextIfdOffset));
2044 // The next IFD offset needs to be bigger than 8
2045 // since the first IFD offset is at least 8.
2046 if (nextIfdOffset > 8 && nextIfdOffset < dataInputStream.mLength) {
2047 dataInputStream.seek(nextIfdOffset);
2048 readImageFileDirectory(dataInputStream, IFD_THUMBNAIL_HINT);
2053 // Gets the corresponding IFD group index of the given tag number for writing Exif Tags.
2054 private static int getIfdHintFromTagNumber(int tagNumber) {
2055 for (int i = 0; i < IFD_POINTER_TAG_HINTS.length; ++i) {
2056 if (IFD_POINTER_TAGS[i].number == tagNumber) {
2057 return IFD_POINTER_TAG_HINTS[i];
2063 // Writes an Exif segment into the given output stream.
2064 private int writeExifSegment(ByteOrderAwarenessDataOutputStream dataOutputStream,
2065 int exifOffsetFromBeginning) throws IOException {
2066 // The following variables are for calculating each IFD tag group size in bytes.
2067 int[] ifdOffsets = new int[EXIF_TAGS.length];
2068 int[] ifdDataSizes = new int[EXIF_TAGS.length];
2070 // Remove IFD pointer tags (we'll re-add it later.)
2071 for (ExifTag tag : IFD_POINTER_TAGS) {
2072 removeAttribute(tag.name);
2074 // Remove old thumbnail data
2075 removeAttribute(JPEG_INTERCHANGE_FORMAT_TAG.name);
2076 removeAttribute(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name);
2078 // Remove null value tags.
2079 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2080 for (Object obj : mAttributes[hint].entrySet().toArray()) {
2081 final Map.Entry entry = (Map.Entry) obj;
2082 if (entry.getValue() == null) {
2083 mAttributes[hint].remove(entry.getKey());
2088 // Add IFD pointer tags. The next offset of primary image TIFF IFD will have thumbnail IFD
2089 // offset when there is one or more tags in the thumbnail IFD.
2090 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
2091 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name,
2092 ExifAttribute.createULong(0, mExifByteOrder));
2094 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
2095 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
2096 ExifAttribute.createULong(0, mExifByteOrder));
2098 if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
2099 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
2100 ExifAttribute.createULong(0, mExifByteOrder));
2102 if (mHasThumbnail) {
2103 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
2104 ExifAttribute.createULong(0, mExifByteOrder));
2105 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_LENGTH_TAG.name,
2106 ExifAttribute.createULong(mThumbnailLength, mExifByteOrder));
2109 // Calculate IFD group data area sizes. IFD group data area is assigned to save the entry
2110 // value which has a bigger size than 4 bytes.
2111 for (int i = 0; i < EXIF_TAGS.length; ++i) {
2113 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[i].entrySet()) {
2114 final ExifAttribute exifAttribute = (ExifAttribute) entry.getValue();
2115 final int size = exifAttribute.size();
2120 ifdDataSizes[i] += sum;
2123 // Calculate IFD offsets.
2125 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2126 if (!mAttributes[hint].isEmpty()) {
2127 ifdOffsets[hint] = position;
2128 position += 2 + mAttributes[hint].size() * 12 + 4 + ifdDataSizes[hint];
2131 if (mHasThumbnail) {
2132 int thumbnailOffset = position;
2133 mAttributes[IFD_TIFF_HINT].put(JPEG_INTERCHANGE_FORMAT_TAG.name,
2134 ExifAttribute.createULong(thumbnailOffset, mExifByteOrder));
2135 mThumbnailOffset = exifOffsetFromBeginning + thumbnailOffset;
2136 position += mThumbnailLength;
2139 // Calculate the total size
2140 int totalSize = position + 8; // eight bytes is for header part.
2142 Log.d(TAG, "totalSize length: " + totalSize);
2143 for (int i = 0; i < EXIF_TAGS.length; ++i) {
2144 Log.d(TAG, String.format("index: %d, offsets: %d, tag count: %d, data sizes: %d",
2145 i, ifdOffsets[i], mAttributes[i].size(), ifdDataSizes[i]));
2149 // Update IFD pointer tags with the calculated offsets.
2150 if (!mAttributes[IFD_EXIF_HINT].isEmpty()) {
2151 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[0].name,
2152 ExifAttribute.createULong(ifdOffsets[IFD_EXIF_HINT], mExifByteOrder));
2154 if (!mAttributes[IFD_GPS_HINT].isEmpty()) {
2155 mAttributes[IFD_TIFF_HINT].put(IFD_POINTER_TAGS[1].name,
2156 ExifAttribute.createULong(ifdOffsets[IFD_GPS_HINT], mExifByteOrder));
2158 if (!mAttributes[IFD_INTEROPERABILITY_HINT].isEmpty()) {
2159 mAttributes[IFD_EXIF_HINT].put(IFD_POINTER_TAGS[2].name, ExifAttribute.createULong(
2160 ifdOffsets[IFD_INTEROPERABILITY_HINT], mExifByteOrder));
2163 // Write TIFF Headers. See JEITA CP-3451C Table 1. page 10.
2164 dataOutputStream.writeUnsignedShort(totalSize);
2165 dataOutputStream.write(IDENTIFIER_EXIF_APP1);
2166 dataOutputStream.writeShort(mExifByteOrder == ByteOrder.BIG_ENDIAN
2167 ? BYTE_ALIGN_MM : BYTE_ALIGN_II);
2168 dataOutputStream.setByteOrder(mExifByteOrder);
2169 dataOutputStream.writeUnsignedShort(0x2a);
2170 dataOutputStream.writeUnsignedInt(8);
2172 // Write IFD groups. See JEITA CP-3451C Figure 7. page 12.
2173 for (int hint = 0; hint < EXIF_TAGS.length; ++hint) {
2174 if (!mAttributes[hint].isEmpty()) {
2175 // See JEITA CP-3451C 4.6.2 IFD structure. page 13.
2176 // Write entry count
2177 dataOutputStream.writeUnsignedShort(mAttributes[hint].size());
2180 int dataOffset = ifdOffsets[hint] + 2 + mAttributes[hint].size() * 12 + 4;
2181 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2182 // Convert tag name to tag number.
2183 final ExifTag tag = (ExifTag) sExifTagMapsForWriting[hint].get(entry.getKey());
2184 final int tagNumber = tag.number;
2185 final ExifAttribute attribute = (ExifAttribute) entry.getValue();
2186 final int size = attribute.size();
2188 dataOutputStream.writeUnsignedShort(tagNumber);
2189 dataOutputStream.writeUnsignedShort(attribute.format);
2190 dataOutputStream.writeInt(attribute.numberOfComponents);
2192 dataOutputStream.writeUnsignedInt(dataOffset);
2195 dataOutputStream.write(attribute.bytes);
2196 // Fill zero up to 4 bytes
2198 for (int i = size; i < 4; ++i) {
2199 dataOutputStream.writeByte(0);
2205 // Write the next offset. It writes the offset of thumbnail IFD if there is one or
2206 // more tags in the thumbnail IFD when the current IFD is the primary image TIFF
2207 // IFD; Otherwise 0.
2208 if (hint == 0 && !mAttributes[IFD_THUMBNAIL_HINT].isEmpty()) {
2209 dataOutputStream.writeUnsignedInt(ifdOffsets[IFD_THUMBNAIL_HINT]);
2211 dataOutputStream.writeUnsignedInt(0);
2214 // Write values of data field exceeding 4 bytes after the next offset.
2215 for (Map.Entry entry : (Set<Map.Entry>) mAttributes[hint].entrySet()) {
2216 ExifAttribute attribute = (ExifAttribute) entry.getValue();
2218 if (attribute.bytes.length > 4) {
2219 dataOutputStream.write(attribute.bytes, 0, attribute.bytes.length);
2226 if (mHasThumbnail) {
2227 dataOutputStream.write(getThumbnail());
2230 // Reset the byte order to big endian in order to write remaining parts of the JPEG file.
2231 dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN);
2237 * Determines the data format of EXIF entry value.
2239 * @param entryValue The value to be determined.
2240 * @return Returns two data formats gussed as a pair in integer. If there is no two candidate
2241 data formats for the given entry value, returns {@code -1} in the second of the pair.
2243 private static Pair<Integer, Integer> guessDataFormat(String entryValue) {
2244 // See TIFF 6.0 spec Types. page 15.
2245 // Take the first component if there are more than one component.
2246 if (entryValue.contains(",")) {
2247 String[] entryValues = entryValue.split(",");
2248 Pair<Integer, Integer> dataFormat = guessDataFormat(entryValues[0]);
2249 if (dataFormat.first == IFD_FORMAT_STRING) {
2252 for (int i = 1; i < entryValues.length; ++i) {
2253 final Pair<Integer, Integer> guessDataFormat = guessDataFormat(entryValues[i]);
2254 int first = -1, second = -1;
2255 if (guessDataFormat.first == dataFormat.first
2256 || guessDataFormat.second == dataFormat.first) {
2257 first = dataFormat.first;
2259 if (dataFormat.second != -1 && (guessDataFormat.first == dataFormat.second
2260 || guessDataFormat.second == dataFormat.second)) {
2261 second = dataFormat.second;
2263 if (first == -1 && second == -1) {
2264 return new Pair<>(IFD_FORMAT_STRING, -1);
2267 dataFormat = new Pair<>(second, -1);
2271 dataFormat = new Pair<>(first, -1);
2278 if (entryValue.contains("/")) {
2279 String[] rationalNumber = entryValue.split("/");
2280 if (rationalNumber.length == 2) {
2282 long numerator = Long.parseLong(rationalNumber[0]);
2283 long denominator = Long.parseLong(rationalNumber[1]);
2284 if (numerator < 0L || denominator < 0L) {
2285 return new Pair<>(IFD_FORMAT_SRATIONAL, - 1);
2287 if (numerator > Integer.MAX_VALUE || denominator > Integer.MAX_VALUE) {
2288 return new Pair<>(IFD_FORMAT_URATIONAL, -1);
2290 return new Pair<>(IFD_FORMAT_SRATIONAL, IFD_FORMAT_URATIONAL);
2291 } catch (NumberFormatException e) {
2295 return new Pair<>(IFD_FORMAT_STRING, -1);
2298 Long longValue = Long.parseLong(entryValue);
2299 if (longValue >= 0 && longValue <= 65535) {
2300 return new Pair<>(IFD_FORMAT_USHORT, IFD_FORMAT_ULONG);
2302 if (longValue < 0) {
2303 return new Pair<>(IFD_FORMAT_SLONG, -1);
2305 return new Pair<>(IFD_FORMAT_ULONG, -1);
2306 } catch (NumberFormatException e) {
2310 Double.parseDouble(entryValue);
2311 return new Pair<>(IFD_FORMAT_DOUBLE, -1);
2312 } catch (NumberFormatException e) {
2315 return new Pair<>(IFD_FORMAT_STRING, -1);
2318 // An input stream to parse EXIF data area, which can be written in either little or big endian
2320 private static class ByteOrderAwarenessDataInputStream extends ByteArrayInputStream {
2321 private static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;
2322 private static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
2324 private ByteOrder mByteOrder = ByteOrder.BIG_ENDIAN;
2325 private final long mLength;
2326 private long mPosition;
2328 public ByteOrderAwarenessDataInputStream(byte[] bytes) {
2330 mLength = bytes.length;
2334 public void setByteOrder(ByteOrder byteOrder) {
2335 mByteOrder = byteOrder;
2338 public void seek(long byteCount) throws IOException {
2341 if (skip(byteCount) != byteCount) {
2342 throw new IOException("Couldn't seek up to the byteCount");
2346 public long peek() {
2350 public void readFully(byte[] buffer) throws IOException {
2351 mPosition += buffer.length;
2352 if (mPosition > mLength) {
2353 throw new EOFException();
2355 if (super.read(buffer, 0, buffer.length) != buffer.length) {
2356 throw new IOException("Couldn't read up to the length of buffer");
2360 public byte readByte() throws IOException {
2362 if (mPosition > mLength) {
2363 throw new EOFException();
2365 int ch = super.read();
2367 throw new EOFException();
2372 public short readShort() throws IOException {
2374 if (mPosition > mLength) {
2375 throw new EOFException();
2377 int ch1 = super.read();
2378 int ch2 = super.read();
2379 if ((ch1 | ch2) < 0) {
2380 throw new EOFException();
2382 if (mByteOrder == LITTLE_ENDIAN) {
2383 return (short) ((ch2 << 8) + (ch1));
2384 } else if (mByteOrder == BIG_ENDIAN) {
2385 return (short) ((ch1 << 8) + (ch2));
2387 throw new IOException("Invalid byte order: " + mByteOrder);
2390 public int readInt() throws IOException {
2392 if (mPosition > mLength) {
2393 throw new EOFException();
2395 int ch1 = super.read();
2396 int ch2 = super.read();
2397 int ch3 = super.read();
2398 int ch4 = super.read();
2399 if ((ch1 | ch2 | ch3 | ch4) < 0) {
2400 throw new EOFException();
2402 if (mByteOrder == LITTLE_ENDIAN) {
2403 return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + ch1);
2404 } else if (mByteOrder == BIG_ENDIAN) {
2405 return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4);
2407 throw new IOException("Invalid byte order: " + mByteOrder);
2411 public long skip(long byteCount) {
2412 long skipped = super.skip(Math.min(byteCount, mLength - mPosition));
2413 mPosition += skipped;
2417 public int readUnsignedShort() throws IOException {
2419 if (mPosition > mLength) {
2420 throw new EOFException();
2422 int ch1 = super.read();
2423 int ch2 = super.read();
2424 if ((ch1 | ch2) < 0) {
2425 throw new EOFException();
2427 if (mByteOrder == LITTLE_ENDIAN) {
2428 return ((ch2 << 8) + (ch1));
2429 } else if (mByteOrder == BIG_ENDIAN) {
2430 return ((ch1 << 8) + (ch2));
2432 throw new IOException("Invalid byte order: " + mByteOrder);
2435 public long readUnsignedInt() throws IOException {
2436 return readInt() & 0xffffffffL;
2439 public long readLong() throws IOException {
2441 if (mPosition > mLength) {
2442 throw new EOFException();
2444 int ch1 = super.read();
2445 int ch2 = super.read();
2446 int ch3 = super.read();
2447 int ch4 = super.read();
2448 int ch5 = super.read();
2449 int ch6 = super.read();
2450 int ch7 = super.read();
2451 int ch8 = super.read();
2452 if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) {
2453 throw new EOFException();
2455 if (mByteOrder == LITTLE_ENDIAN) {
2456 return (((long) ch8 << 56) + ((long) ch7 << 48) + ((long) ch6 << 40)
2457 + ((long) ch5 << 32) + ((long) ch4 << 24) + ((long) ch3 << 16)
2458 + ((long) ch2 << 8) + ch1);
2459 } else if (mByteOrder == BIG_ENDIAN) {
2460 return (((long) ch1 << 56) + ((long) ch2 << 48) + ((long) ch3 << 40)
2461 + ((long) ch4 << 32) + ((long) ch5 << 24) + ((long) ch6 << 16)
2462 + ((long) ch7 << 8) + ch8);
2464 throw new IOException("Invalid byte order: " + mByteOrder);
2467 public float readFloat() throws IOException {
2468 return Float.intBitsToFloat(readInt());
2471 public double readDouble() throws IOException {
2472 return Double.longBitsToDouble(readLong());
2476 // An output stream to write EXIF data area, which can be written in either little or big endian
2478 private static class ByteOrderAwarenessDataOutputStream extends FilterOutputStream {
2479 private final OutputStream mOutputStream;
2480 private ByteOrder mByteOrder;
2482 public ByteOrderAwarenessDataOutputStream(OutputStream out, ByteOrder byteOrder) {
2484 mOutputStream = out;
2485 mByteOrder = byteOrder;
2488 public void setByteOrder(ByteOrder byteOrder) {
2489 mByteOrder = byteOrder;
2492 public void write(byte[] bytes) throws IOException {
2493 mOutputStream.write(bytes);
2496 public void write(byte[] bytes, int offset, int length) throws IOException {
2497 mOutputStream.write(bytes, offset, length);
2500 public void writeByte(int val) throws IOException {
2501 mOutputStream.write(val);
2504 public void writeShort(short val) throws IOException {
2505 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
2506 mOutputStream.write((val >>> 0) & 0xFF);
2507 mOutputStream.write((val >>> 8) & 0xFF);
2508 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
2509 mOutputStream.write((val >>> 8) & 0xFF);
2510 mOutputStream.write((val >>> 0) & 0xFF);
2514 public void writeInt(int val) throws IOException {
2515 if (mByteOrder == ByteOrder.LITTLE_ENDIAN) {
2516 mOutputStream.write((val >>> 0) & 0xFF);
2517 mOutputStream.write((val >>> 8) & 0xFF);
2518 mOutputStream.write((val >>> 16) & 0xFF);
2519 mOutputStream.write((val >>> 24) & 0xFF);
2520 } else if (mByteOrder == ByteOrder.BIG_ENDIAN) {
2521 mOutputStream.write((val >>> 24) & 0xFF);
2522 mOutputStream.write((val >>> 16) & 0xFF);
2523 mOutputStream.write((val >>> 8) & 0xFF);
2524 mOutputStream.write((val >>> 0) & 0xFF);
2528 public void writeUnsignedShort(int val) throws IOException {
2529 writeShort((short) val);
2532 public void writeUnsignedInt(long val) throws IOException {
2533 writeInt((int) val);