/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package it.sephiroth.android.library.exif2; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.SystemClock; import android.util.Log; import android.util.SparseIntArray; import org.apache.commons.io.IOUtils; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteOrder; import java.nio.channels.FileChannel; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.TimeZone; /** * This class provides methods and constants for reading and writing jpeg file * metadata. It contains a collection of ExifTags, and a collection of * definitions for creating valid ExifTags. The collection of ExifTags can be * updated by: reading new ones from a file, deleting or adding existing ones, * or building new ExifTags from a tag definition. These ExifTags can be written * to a valid jpeg image as exif metadata. * <p/> * Each ExifTag has a tag ID (TID) and is stored in a specific image file * directory (IFD) as specified by the exif standard. A tag definition can be * looked up with a constant that is a combination of TID and IFD. This * definition has information about the type, number of components, and valid * IFDs for a tag. * * @see ExifTag */ public class ExifInterface { private static final String TAG = "ExifInterface"; public static final int TAG_NULL = - 1; public static final int IFD_NULL = - 1; public static final int DEFINITION_NULL = 0; /** * Tag constants for Jeita EXIF 2.2 */ // IFD 0 public static final int TAG_IMAGE_WIDTH = defineTag( IfdId.TYPE_IFD_0, (short) 0x0100 ); public static final int TAG_IMAGE_LENGTH = defineTag( IfdId.TYPE_IFD_0, (short) 0x0101 ); // Image height public static final int TAG_BITS_PER_SAMPLE = defineTag( IfdId.TYPE_IFD_0, (short) 0x0102 ); /** * Value is unsigned int.<br /> * (Read only tag) The compression scheme used for the image data. When a primary image is JPEG compressed, this designation is * not necessary and is omitted. When thumbnails use JPEG compression, this tag value is set to 6. * <ul> * <li>1 = uncompressed</li> * <li>6 = JPEG compression (thumbnails only)</li> * <li>Other = reserved</li> */ public static final int TAG_COMPRESSION = defineTag( IfdId.TYPE_IFD_0, (short) 0x0103 ); public static final int TAG_PHOTOMETRIC_INTERPRETATION = defineTag( IfdId.TYPE_IFD_0, (short) 0x0106 ); public static final int TAG_IMAGE_DESCRIPTION = defineTag( IfdId.TYPE_IFD_0, (short) 0x010E ); /** * Value is ascii string<br /> * The manufacturer of the recording equipment. This is the manufacturer of the DSC, scanner, video digitizer or other equipment * that generated the image. When the field is left blank, it is treated as unknown. */ public static final int TAG_MAKE = defineTag( IfdId.TYPE_IFD_0, (short) 0x010F ); /** * Value is ascii string<br /> * The model name or model number of the equipment. This is the model name of number of the DSC, scanner, video digitizer or * other equipment that generated the image. When the field is left blank, it is treated as unknown. */ public static final int TAG_MODEL = defineTag( IfdId.TYPE_IFD_0, (short) 0x0110 ); public static final int TAG_STRIP_OFFSETS = defineTag( IfdId.TYPE_IFD_0, (short) 0x0111 ); /** * Value is int<br /> * The orientation of the camera relative to the scene, when the image was captured. The start point of stored data is: * <ul> * <li>'0' undefined</li> * <li>'1' normal</li> * <li>'2' flip horizontal</li> * <li>'3' rotate 180</li> * <li>'4' flip vertical</li> * <li>'5' transpose, flipped about top-left <--> bottom-right axis</li> * <li>'6' rotate 90 cw</li> * <li>'7' transverse, flipped about top-right <--> bottom-left axis</li> * <li>'8' rotate 270</li> * <li>'9' undefined</li> * </ul> */ public static final int TAG_ORIENTATION = defineTag( IfdId.TYPE_IFD_0, (short) 0x0112 ); public static final int TAG_SAMPLES_PER_PIXEL = defineTag( IfdId.TYPE_IFD_0, (short) 0x0115 ); public static final int TAG_ROWS_PER_STRIP = defineTag( IfdId.TYPE_IFD_0, (short) 0x0116 ); public static final int TAG_STRIP_BYTE_COUNTS = defineTag( IfdId.TYPE_IFD_0, (short) 0x0117 ); public static final int TAG_INTEROP_VERSION = defineTag(IfdId.TYPE_IFD_INTEROPERABILITY, (short)0x0002); /** * Value is unsigned double.<br /> * Display/Print resolution of image. Large number of digicam uses 1/72inch, but it has no mean because personal computer doesn't * use this value to display/print out. */ public static final int TAG_X_RESOLUTION = defineTag( IfdId.TYPE_IFD_0, (short) 0x011A ); /** * @see #TAG_X_RESOLUTION */ public static final int TAG_Y_RESOLUTION = defineTag( IfdId.TYPE_IFD_0, (short) 0x011B ); public static final int TAG_PLANAR_CONFIGURATION = defineTag( IfdId.TYPE_IFD_0, (short) 0x011C ); /** * Value is unsigned int.<br /> * Unit of XResolution(0x011a)/YResolution(0x011b) * <ul> * <li>'1' means no-unit ( use inch )</li> * <li>'2' inch</li> * <li>'3' centimeter</li> * <li>'4' millimeter</li> * <li>'5' micrometer</li> * </ul> */ public static final int TAG_RESOLUTION_UNIT = defineTag( IfdId.TYPE_IFD_0, (short) 0x0128 ); public static final int TAG_TRANSFER_FUNCTION = defineTag( IfdId.TYPE_IFD_0, (short) 0x012D ); /** * Value is ascii string<br /> * Shows firmware(internal software of digicam) version number. */ public static final int TAG_SOFTWARE = defineTag( IfdId.TYPE_IFD_0, (short) 0x0131 ); /** * Value is ascii string (20)<br /> * Date/Time of image was last modified. Data format is "YYYY:MM:DD HH:MM:SS"+0x00, total 20bytes. In usual, it has the same * value of DateTimeOriginal(0x9003) */ public static final int TAG_DATE_TIME = defineTag( IfdId.TYPE_IFD_0, (short) 0x0132 ); /** * Vallue is ascii String<br /> * This tag records the name of the camera owner, photographer or image creator. The detailed format is not specified, but it is * recommended that the information be written as in the example below for ease of Interoperability. When the field is left * blank, it is treated as unknown. */ public static final int TAG_ARTIST = defineTag( IfdId.TYPE_IFD_0, (short) 0x013B ); public static final int TAG_WHITE_POINT = defineTag( IfdId.TYPE_IFD_0, (short) 0x013E ); public static final int TAG_PRIMARY_CHROMATICITIES = defineTag( IfdId.TYPE_IFD_0, (short) 0x013F ); public static final int TAG_Y_CB_CR_COEFFICIENTS = defineTag( IfdId.TYPE_IFD_0, (short) 0x0211 ); public static final int TAG_Y_CB_CR_SUB_SAMPLING = defineTag( IfdId.TYPE_IFD_0, (short) 0x0212 ); public static final int TAG_Y_CB_CR_POSITIONING = defineTag( IfdId.TYPE_IFD_0, (short) 0x0213 ); public static final int TAG_REFERENCE_BLACK_WHITE = defineTag( IfdId.TYPE_IFD_0, (short) 0x0214 ); /** * Values is ascii string<br /> * Shows copyright information */ public static final int TAG_COPYRIGHT = defineTag( IfdId.TYPE_IFD_0, (short) 0x8298 ); public static final int TAG_EXIF_IFD = defineTag( IfdId.TYPE_IFD_0, (short) 0x8769 ); public static final int TAG_GPS_IFD = defineTag( IfdId.TYPE_IFD_0, (short) 0x8825 ); // IFD 1 public static final int TAG_JPEG_INTERCHANGE_FORMAT = defineTag( IfdId.TYPE_IFD_1, (short) 0x0201 ); public static final int TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = defineTag( IfdId.TYPE_IFD_1, (short) 0x0202 ); // IFD Exif Tags /** * Value is unsigned double<br /> * Exposure time (reciprocal of shutter speed). Unit is second */ public static final int TAG_EXPOSURE_TIME = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x829A ); /** * Value is unsigned double<br /> * The actual F-number(F-stop) of lens when the image was taken * * @see #TAG_APERTURE_VALUE */ public static final int TAG_F_NUMBER = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x829D ); /** * Value is unsigned int.<br /> * Exposure program that the camera used when image was taken. * <ul> * <li>'1' means manual control</li> * <li>'2' program normal</li> * <li>'3' aperture priority</li> * <li>'4' shutter priority</li> * <li>'5' program creative (slow program)</li> * <li>'6' program action(high-speed program)</li> * <li>'7' portrait mode</li> * <li>'8' landscape mode.</li> * </ul> */ public static final int TAG_EXPOSURE_PROGRAM = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x8822 ); public static final int TAG_SPECTRAL_SENSITIVITY = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x8824 ); /** * Value is unsigned int.<br /> * CCD sensitivity equivalent to Ag-Hr film speedrate.<br /> * Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232 */ public static final int TAG_ISO_SPEED_RATINGS = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x8827 ); public static final int TAG_OECF = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x8828 ); /** * ASCII string (4).<br /> * The version of this standard supported. Nonexistence of this field is taken to mean nonconformance to the standard (see * section 4.2). Conformance to this standard is indicated by recording "0220" as 4-byte ASCII */ public static final int TAG_EXIF_VERSION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9000 ); /** * Value is ascii string (20)<br /> * Date/Time of original image taken. This value should not be modified by user program. */ public static final int TAG_DATE_TIME_ORIGINAL = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9003 ); /** * Value is ascii string (20)<br /> * Date/Time of image digitized. Usually, it contains the same value of DateTimeOriginal(0x9003). */ public static final int TAG_DATE_TIME_DIGITIZED = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9004 ); public static final int TAG_COMPONENTS_CONFIGURATION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9101 ); public static final int TAG_COMPRESSED_BITS_PER_PIXEL = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9102 ); /** * Value is signed double.<br /> * Shutter speed. To convert this value to ordinary 'Shutter Speed'; calculate this value's power of 2, then reciprocal. For * example, if value is '4', shutter speed is 1/(2^4)=1/16 second. */ public static final int TAG_SHUTTER_SPEED_VALUE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9201 ); /** * Value is unsigned double<br /> * The actual aperture value of lens when the image was taken.<br /> * To convert this value to ordinary F-number(F-stop), calculate this value's power of root 2 (=1.4142).<br /> * For example, if value is '5', F-number is 1.4142^5 = F5.6<br /> * <p/> * <pre> * FNumber = Math.exp( ApertureValue * Math.log( 2 ) * 0.5 ); * </pre> * * @see #TAG_F_NUMBER */ public static final int TAG_APERTURE_VALUE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9202 ); /** * Value is signed double<br /> * Brightness of taken subject, unit is EV. */ public static final int TAG_BRIGHTNESS_VALUE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9203 ); /** * Value is signed double.<br /> * The exposure bias. The unit is the APEX value. Ordinarily it is given in the range of -99.99 to 99.99 */ public static final int TAG_EXPOSURE_BIAS_VALUE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9204 ); /** * Value is unsigned double.<br /> * Maximum aperture value of lens.<br /> * You can convert to F-number by calculating power of root 2 (same process of ApertureValue(0x9202).<br /> * <p/> * <pre> * FNumber = Math.exp( MaxApertureValue * Math.log( 2 ) * 0.5 ) * </pre> */ public static final int TAG_MAX_APERTURE_VALUE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9205 ); /** * Value if signed double.<br /> * Distance to focus point, unit is meter. If value < 0 then focus point is infinite */ public static final int TAG_SUBJECT_DISTANCE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9206 ); /** * Value is unsigned int.<br /> * Exposure metering method: * <ul> * <li>0 = unknown</li> * <li>1 = Average</li> * <li>2 = CenterWeightedAverage</li> * <li>3 = Spot</li> * <li>4 = MultiSpot</li> * <li>5 = Pattern</li> * <li>6 = Partial</li> * <li>Other = reserved</li> * <li>255 = other</li> * </ul> */ public static final int TAG_METERING_MODE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9207 ); /** * Value is unsigned int.<br /> * Light source, actually this means white balance setting. * <ul> * <li>0 = means auto</li> * <li>1 = Daylight</li> * <li>2 = Fluorescent</li> * <li>3 = Tungsten (incandescent light)</li> * <li>4 = Flash</li> * <li>9 = Fine weather</li> * <li>10 = Cloudy weather</li> * <li>11 = Shade</li> * <li>12 = Daylight fluorescent (D 5700 - 7100K)</li> * <li>13 = Day white fluorescent (N 4600 - 5400K)</li> * <li>14 = Cool white fluorescent (W 3900 - 4500K)</li> * <li>15 = White fluorescent (WW 3200 - 3700K)</li> * <li>17 = Standard light A</li> * <li>18 = Standard light B</li> * <li>19 = Standard light C</li> * <li>20 = D55</li> * <li>21 = D65</li> * <li>22 = D75</li> * <li>23 = D50</li> * <li>24 = ISO studio tungsten</li> * <li>255 = other light source</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_LIGHT_SOURCE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9208 ); /** * Value is unsigned integer<br /> * The 8 bits can be extracted and evaluated in this way:<br /> * <ol> * <li>Bit 0 indicates the flash firing status</li> * <li>bits 1 and 2 indicate the flash return status</li> * <li>bits 3 and 4 indicate the flash mode</li> * <li>bit 5 indicates whether the flash function is present</li> * <li>and bit 6 indicates "red eye" mode</li> * <li>bit 7 unused</li> * </ol> * <p/> * Resulting Flash tag values are:<br /> * <ul> * <li>0000.H = Flash did not fire</li> * <li>0001.H = Flash fired</li> * <li>0005.H = Strobe return light not detected</li> * <li>0007.H = Strobe return light detected</li> * <li>0009.H = Flash fired, compulsory flash mode</li> * <li>000D.H = Flash fired, compulsory flash mode, return light not detected</li> * <li>000F.H = Flash fired, compulsory flash mode, return light detected</li> * <li>0010.H = Flash did not fire, compulsory flash mode</li> * <li>0018.H = Flash did not fire, auto mode</li> * <li>0019.H = Flash fired, auto mode</li> * <li>001D.H = Flash fired, auto mode, return light not detected</li> * <li>001F.H = Flash fired, auto mode, return light detected</li> * <li>0020.H = No flash function</li> * <li>0041.H = Flash fired, red-eye reduction mode</li> * <li>0045.H = Flash fired, red-eye reduction mode, return light not detected</li> * <li>0047.H = Flash fired, red-eye reduction mode, return light detected</li> * <li>0049.H = Flash fired, compulsory flash mode, red-eye reduction mode</li> * <li>004D.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected</li> * <li>004F.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light detected</li> * <li>0059.H = Flash fired, auto mode, red-eye reduction mode</li> * <li>005D.H = Flash fired, auto mode, return light not detected, red-eye reduction mode</li> * <li>005F.H = Flash fired, auto mode, return light detected, red-eye reduction mode</li> * <li>Other = reserved</li> * </ul> * * @see <a href="http://www.exif.org/Exif2-2.PDF">http://www.exif.org/Exif2-2.PDF</a> */ public static final int TAG_FLASH = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9209 ); /** * Value is unsigned double<br /> * Focal length of lens used to take image. Unit is millimeter. */ public static final int TAG_FOCAL_LENGTH = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x920A ); public static final int TAG_SUBJECT_AREA = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9214 ); public static final int TAG_MAKER_NOTE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x927C ); public static final int TAG_USER_COMMENT = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9286 ); public static final int TAG_SUB_SEC_TIME = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9290 ); public static final int TAG_SUB_SEC_TIME_ORIGINAL = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9291 ); public static final int TAG_SUB_SEC_TIME_DIGITIZED = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x9292 ); public static final int TAG_FLASHPIX_VERSION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA000 ); /** * Value is int.<br /> * Normally sRGB (=1) is used to define the color space based on the PC monitor conditions and environment. If a color space * other than sRGB is used, Uncalibrated (=FFFF.H) is set. Image data recorded as Uncalibrated can be treated as sRGB when it is * converted to Flashpix. On sRGB see Annex E. * <ul> * <li>'1' = sRGB</li> * <li>'FFFF' = Uncalibrated</li> * <li>'other' = Reserved</li> * </ul> */ public static final int TAG_COLOR_SPACE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA001 ); /** * Value is unsigned int.<br /> * Specific to compressed data; the valid width of the meaningful image. When a compressed file is recorded, the valid width of * the meaningful image shall be recorded in this tag, whether or not there is padding data or a restart marker. This tag should * not exist in an uncompressed file. */ public static final int TAG_PIXEL_X_DIMENSION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA002 ); /** * @see #TAG_PIXEL_X_DIMENSION */ public static final int TAG_PIXEL_Y_DIMENSION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA003 ); public static final int TAG_RELATED_SOUND_FILE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA004 ); public static final int TAG_INTEROPERABILITY_IFD = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA005 ); public static final int TAG_FLASH_ENERGY = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA20B ); public static final int TAG_SPATIAL_FREQUENCY_RESPONSE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA20C ); /** * Value is unsigned double.<br /> * Indicates the number of pixels in the image width (X) direction per FocalPlaneResolutionUnit on the camera focal plane. CCD's * pixel density * * @see #TAG_FOCAL_PLANE_RESOLUTION_UNIT */ public static final int TAG_FOCAL_PLANE_X_RESOLUTION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA20E ); /** * @see #TAG_FOCAL_PLANE_X_RESOLUTION */ public static final int TAG_FOCAL_PLANE_Y_RESOLUTION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA20F ); /** * Value is unsigned int.<br /> * Unit of FocalPlaneXResoluton/FocalPlaneYResolution. * <ul> * <li>'1' means no-unit</li> * <li>'2' inch</li> * <li>'3' centimeter</li> * <li>'4' millimeter</li> * <li>'5' micrometer</li> * </ul> * <p/> * This tag can be used to calculate the CCD Width: * <p/> * <pre> * CCDWidth = ( PixelXDimension * FocalPlaneResolutionUnit / FocalPlaneXResolution ) * </pre> */ public static final int TAG_FOCAL_PLANE_RESOLUTION_UNIT = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA210 ); public static final int TAG_SUBJECT_LOCATION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA214 ); public static final int TAG_EXPOSURE_INDEX = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA215 ); /** * Value is unsigned int.<br /> * Indicates the image sensor type on the camera or input device. The values are as follows: * <ul> * <li>1 = Not defined</li> * <li>2 = One-chip color area sensor</li> * <li>3 = Two-chip color area sensor JEITA CP-3451 - 41</li> * <li>4 = Three-chip color area sensor</li> * <li>5 = Color sequential area sensor</li> * <li>7 = Trilinear sensor</li> * <li>8 = Color sequential linear sensor</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_SENSING_METHOD = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA217 ); public static final int TAG_FILE_SOURCE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA300 ); public static final int TAG_SCENE_TYPE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA301 ); public static final int TAG_CFA_PATTERN = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA302 ); public static final int TAG_CUSTOM_RENDERED = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA401 ); /** * Value is int.<br /> * This tag indicates the exposure mode set when the image was shot. In auto-bracketing mode, the camera shoots a series of * frames of the same scene at different exposure settings. * <ul> * <li>0 = Auto exposure</li> * <li>1 = Manual exposure</li> * <li>2 = Auto bracket</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_EXPOSURE_MODE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA402 ); public static final int TAG_WHITE_BALANCE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA403 ); /** * Value is double.<br /> * This tag indicates the digital zoom ratio when the image was shot. If the numerator of the recorded value is 0, this indicates * that digital zoom was not used */ public static final int TAG_DIGITAL_ZOOM_RATIO = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA404 ); /** * Value is unsigned int.<br /> * This tag indicates the equivalent focal length assuming a 35mm film camera, in mm.<br /> * Exif 2.2 tag, usually not present, it can be calculated by: * <p/> * <pre> * CCDWidth = ( PixelXDimension * FocalplaneUnits / FocalplaneXRes ); * FocalLengthIn35mmFilm = ( FocalLength / CCDWidth * 36 + 0.5 ); * </pre> */ public static final int TAG_FOCAL_LENGTH_IN_35_MM_FILE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA405 ); /** * Value is int.<br /> * This tag indicates the type of scene that was shot. It can also be used to record the mode in which the image was shot. Note * that this differs from the scene type (SceneType) tag. * <ul> * <li>0 = Standard</li> * <li>1 = Landscape</li> * <li>2 = Portrait</li> * <li>3 = Night scene</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_SCENE_CAPTURE_TYPE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA406 ); /** * Value is int.<br /> * This tag indicates the degree of overall image gain adjustment. * <ul> * <li>0 = None</li> * <li>1 = Low gain up</li> * <li>2 = High gain up</li> * <li>3 = Low gain down</li> * <li>4 = High gain down</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_GAIN_CONTROL = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA407 ); /** * Value is int.<br /> * This tag indicates the direction of contrast processing applied by the camera when the image was shot. * <ul> * <li>0 = Normal</li> * <li>1 = Soft</li> * <li>2 = Hard</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_CONTRAST = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA408 ); /** * Value is int.<br /> * This tag indicates the direction of saturation processing applied by the camera when the image was shot. * <ul> * <li>0 = Normal</li> * <li>1 = Low saturation</li> * <li>2 = High saturation</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_SATURATION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA409 ); /** * Value is int.<br /> * This tag indicates the direction of sharpness processing applied by the camera when the image was shot * <ul> * <li>0 = Normal</li> * <li>1 = Soft</li> * <li>2 = Hard</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_SHARPNESS = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA40A ); public static final int TAG_DEVICE_SETTING_DESCRIPTION = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA40B ); /** * Value is int.<br /> * This tag indicates the distance to the subject. * <ul> * <li>0 = unknown</li> * <li>1 = Macro</li> * <li>2 = Close view</li> * <li>3 = Distant view</li> * <li>Other = reserved</li> * </ul> */ public static final int TAG_SUBJECT_DISTANCE_RANGE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA40C ); /** * {@link ExifTag#TYPE_ASCII} */ public static final int TAG_IMAGE_UNIQUE_ID = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA420 ); /** * Lens Specifications. The value it's a 4 rational containing: * <ol> * <li>Minimum focal length (in mm)</li> * <li>Maximum focal length (in mm)</li> * <li>Minimum F Number in the minimum focal length</li> * <li>Maximum F Number in the maximum focal length</li> * </ol> * * {@link ExifTag#TYPE_RATIONAL} * @since EXIF 2.3 * @see it.sephiroth.android.library.exif2.ExifUtil#processLensSpecifications(Rational[]) */ public static final int TAG_LENS_SPECS = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA432 ); /** * Lens maker * {@link ExifTag#TYPE_ASCII} * @since EXIF 2.3 */ public static final int TAG_LENS_MAKE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA433 ); /** * Lens model name and number * {@link ExifTag#TYPE_ASCII} * @since EXIF 2.3 */ public static final int TAG_LENS_MODEL = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0xA434 ); /** * The SensitivityType tag indicates which one of the parameters of ISO12232 is the * PhotographicSensitivity tag. Although it is an optional tag, it should be recorded * when a PhotographicSensitivity tag is recorded. * Value = 4, 5, 6, or 7 may be used in case that the values of plural * parameters are the same.<br/> * Values: * <ul> * <li>0: Unknown</li> * <li>1: Standardoutputsensitivity(SOS)</li> * <li>2: Recommended exposure index (REI)</li> * <li>3: ISOspeed</li> * <li>4: Standard output sensitivity (SOS) and recommended exposure index (REI)</li> * <li>5: Standardoutputsensitivity(SOS)andISOspeed</li> * <li>6: Recommendedexposureindex(REI)andISOspeed</li> * <li>7: Standard output sensitivity (SOS) and recommended exposure index (REI) and ISO speed</li> * <li>Other: Reserved</li> * </ul> * * {@link ExifTag#TYPE_UNSIGNED_SHORT} * @see it.sephiroth.android.library.exif2.ExifInterface.SensitivityType * @since EXIF 2.3 */ public static final int TAG_SENSITIVITY_TYPE = defineTag( IfdId.TYPE_IFD_EXIF, (short) 0x8830 ); // IFD GPS tags public static final int TAG_GPS_VERSION_ID = defineTag( IfdId.TYPE_IFD_GPS, (short) 0 ); /** * Value is string(1)<br /> * Indicates whether the latitude is north or south latitude. The ASCII value 'N' indicates north latitude, and 'S' is south latitude. */ public static final int TAG_GPS_LATITUDE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 1 ); /** * Value is string.<br /> * Indicates the latitude. The latitude is expressed as three RATIONAL values giving the degrees, minutes, and * seconds, respectively. If latitude is expressed as degrees, minutes and seconds, a typical format would be * dd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two * decimal places, the format would be dd/1,mmmm/100,0/1. */ public static final int TAG_GPS_LATITUDE = defineTag( IfdId.TYPE_IFD_GPS, (short) 2 ); /** * Value is string(1)<br /> * Indicates whether the longitude is east or west longitude. ASCII 'E' indicates east longitude, and 'W' is west longitude. */ public static final int TAG_GPS_LONGITUDE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 3 ); /** * Value is string.<br /> * Indicates the longitude. The longitude is expressed as three RATIONAL values giving the degrees, minutes, and * seconds, respectively. If longitude is expressed as degrees, minutes and seconds, a typical format would be * ddd/1,mm/1,ss/1. When degrees and minutes are used and, for example, fractions of minutes are given up to two * decimal places, the format would be ddd/1,mmmm/100,0/1. */ public static final int TAG_GPS_LONGITUDE = defineTag( IfdId.TYPE_IFD_GPS, (short) 4 ); /** * Value is byte<br /> * Indicates the altitude used as the reference altitude. If the reference is sea level and the altitude is above sea level, * 0 is given. If the altitude is below sea level, a value of 1 is given and the altitude is indicated as an absolute value in * the GPSAltitude tag. The reference unit is meters. Note that this tag is BYTE type, unlike other reference tags */ public static final int TAG_GPS_ALTITUDE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 5 ); /** * Value is string.<br /> * Indicates the altitude based on the reference in GPSAltitudeRef. Altitude is expressed as one RATIONAL value. The reference unit is meters. */ public static final int TAG_GPS_ALTITUDE = defineTag( IfdId.TYPE_IFD_GPS, (short) 6 ); public static final int TAG_GPS_TIME_STAMP = defineTag( IfdId.TYPE_IFD_GPS, (short) 7 ); public static final int TAG_GPS_SATTELLITES = defineTag( IfdId.TYPE_IFD_GPS, (short) 8 ); public static final int TAG_GPS_STATUS = defineTag( IfdId.TYPE_IFD_GPS, (short) 9 ); public static final int TAG_GPS_MEASURE_MODE = defineTag( IfdId.TYPE_IFD_GPS, (short) 10 ); public static final int TAG_GPS_DOP = defineTag( IfdId.TYPE_IFD_GPS, (short) 11 ); /** * Value is string(1).<br /> * Indicates the unit used to express the GPS receiver speed of movement. 'K' 'M' and 'N' represents kilometers per hour, miles per hour, and knots. */ public static final int TAG_GPS_SPEED_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 12 ); /** * Value is string.<br /> * Indicates the speed of GPS receiver movement */ public static final int TAG_GPS_SPEED = defineTag( IfdId.TYPE_IFD_GPS, (short) 13 ); public static final int TAG_GPS_TRACK_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 14 ); public static final int TAG_GPS_TRACK = defineTag( IfdId.TYPE_IFD_GPS, (short) 15 ); public static final int TAG_GPS_IMG_DIRECTION_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 16 ); public static final int TAG_GPS_IMG_DIRECTION = defineTag( IfdId.TYPE_IFD_GPS, (short) 17 ); public static final int TAG_GPS_MAP_DATUM = defineTag( IfdId.TYPE_IFD_GPS, (short) 18 ); public static final int TAG_GPS_DEST_LATITUDE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 19 ); public static final int TAG_GPS_DEST_LATITUDE = defineTag( IfdId.TYPE_IFD_GPS, (short) 20 ); public static final int TAG_GPS_DEST_LONGITUDE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 21 ); public static final int TAG_GPS_DEST_LONGITUDE = defineTag( IfdId.TYPE_IFD_GPS, (short) 22 ); public static final int TAG_GPS_DEST_BEARING_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 23 ); public static final int TAG_GPS_DEST_BEARING = defineTag( IfdId.TYPE_IFD_GPS, (short) 24 ); public static final int TAG_GPS_DEST_DISTANCE_REF = defineTag( IfdId.TYPE_IFD_GPS, (short) 25 ); public static final int TAG_GPS_DEST_DISTANCE = defineTag( IfdId.TYPE_IFD_GPS, (short) 26 ); public static final int TAG_GPS_PROCESSING_METHOD = defineTag( IfdId.TYPE_IFD_GPS, (short) 27 ); public static final int TAG_GPS_AREA_INFORMATION = defineTag( IfdId.TYPE_IFD_GPS, (short) 28 ); public static final int TAG_GPS_DATE_STAMP = defineTag( IfdId.TYPE_IFD_GPS, (short) 29 ); public static final int TAG_GPS_DIFFERENTIAL = defineTag( IfdId.TYPE_IFD_GPS, (short) 30 ); // IFD Interoperability tags public static final int TAG_INTEROPERABILITY_INDEX = defineTag( IfdId.TYPE_IFD_INTEROPERABILITY, (short) 1 ); public static final ByteOrder DEFAULT_BYTE_ORDER = ByteOrder.BIG_ENDIAN; private ExifData mData = new ExifData( DEFAULT_BYTE_ORDER ); private static final String NULL_ARGUMENT_STRING = "Argument is null"; private static final String GPS_DATE_FORMAT_STR = "yyyy:MM:dd"; private static final DateFormat mGPSDateStampFormat = new SimpleDateFormat( GPS_DATE_FORMAT_STR ); private static final String DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss"; private static final DateFormat mDateTimeStampFormat = new SimpleDateFormat( DATETIME_FORMAT_STR ); /** * Tags that contain offset markers. These are included in the banned * defines. */ private static HashSet<Short> sOffsetTags = new HashSet<Short>(); static { sOffsetTags.add( getTrueTagKey( TAG_GPS_IFD ) ); sOffsetTags.add( getTrueTagKey( TAG_EXIF_IFD ) ); sOffsetTags.add( getTrueTagKey( TAG_JPEG_INTERCHANGE_FORMAT ) ); sOffsetTags.add( getTrueTagKey( TAG_INTEROPERABILITY_IFD ) ); sOffsetTags.add( getTrueTagKey( TAG_STRIP_OFFSETS ) ); } /** * Tags with definitions that cannot be overridden (banned defines). */ protected static HashSet<Short> sBannedDefines = new HashSet<Short>( sOffsetTags ); static { sBannedDefines.add( getTrueTagKey( TAG_NULL ) ); sBannedDefines.add( getTrueTagKey( TAG_JPEG_INTERCHANGE_FORMAT_LENGTH ) ); sBannedDefines.add( getTrueTagKey( TAG_STRIP_BYTE_COUNTS ) ); } private final Calendar mGPSTimeStampCalendar = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); private SparseIntArray mTagInfo = null; public ExifInterface() { mGPSDateStampFormat.setTimeZone( TimeZone.getTimeZone( "UTC" ) ); } /** * Returns true if tag TID is one of the following: {@link #TAG_EXIF_IFD}, * {@link #TAG_GPS_IFD}, {@link #TAG_JPEG_INTERCHANGE_FORMAT}, * {@link #TAG_STRIP_OFFSETS}, {@link #TAG_INTEROPERABILITY_IFD} * <p/> * Note: defining tags with these TID's is disallowed. * * @param tag a tag's TID (can be obtained from a defined tag constant with * {@link #getTrueTagKey}). * @return true if the TID is that of an offset tag. */ protected static boolean isOffsetTag( short tag ) { return sOffsetTags.contains( tag ); } /** * Returns the Orientation ExifTag value for a given number of degrees. * * @param degrees the amount an image is rotated in degrees. */ public static short getOrientationValueForRotation( int degrees ) { degrees %= 360; if( degrees < 0 ) { degrees += 360; } if( degrees < 90 ) { return Orientation.TOP_LEFT; // 0 degrees } else if( degrees < 180 ) { return Orientation.RIGHT_TOP; // 90 degrees cw } else if( degrees < 270 ) { return Orientation.BOTTOM_LEFT; // 180 degrees } else { return Orientation.RIGHT_BOTTOM; // 270 degrees cw } } /** * Returns the rotation degrees corresponding to an ExifTag Orientation * value. * * @param orientation the ExifTag Orientation value. */ @SuppressWarnings( "unused" ) public static int getRotationForOrientationValue( short orientation ) { switch( orientation ) { case Orientation.TOP_LEFT: return 0; case Orientation.RIGHT_TOP: return 90; case Orientation.BOTTOM_LEFT: return 180; case Orientation.RIGHT_BOTTOM: return 270; default: return 0; } } /** * Given the value from {@link #TAG_FOCAL_PLANE_RESOLUTION_UNIT} or {@link #TAG_RESOLUTION_UNIT} * this method will return the corresponding value in millimeters * * @param resolution {@link #TAG_FOCAL_PLANE_RESOLUTION_UNIT} or {@link #TAG_RESOLUTION_UNIT} * @return resolution in millimeters */ @SuppressWarnings( "unused" ) public double getResolutionUnit( int resolution ) { switch( resolution ) { case 1: case ResolutionUnit.INCHES: return 25.4; case ResolutionUnit.CENTIMETERS: return 10; case ResolutionUnit.MILLIMETERS: return 1; case ResolutionUnit.MICROMETERS: return .001; default: return 25.4; } } /** * Gets the double representation of the GPS latitude or longitude * coordinate. * * @param coordinate an array of 3 Rationals representing the degrees, * minutes, and seconds of the GPS location as defined in the * exif specification. * @param reference a GPS reference reperesented by a String containing "N", * "S", "E", or "W". * @return the GPS coordinate represented as degrees + minutes/60 + * seconds/3600 */ public static double convertLatOrLongToDouble( Rational[] coordinate, String reference ) { try { double degrees = coordinate[0].toDouble(); double minutes = coordinate[1].toDouble(); double seconds = coordinate[2].toDouble(); double result = degrees + minutes / 60.0 + seconds / 3600.0; if( ( reference.startsWith( "S" ) || reference.startsWith( "W" ) ) ) { return - result; } return result; } catch( ArrayIndexOutOfBoundsException e ) { throw new IllegalArgumentException(); } } protected static int[] getAllowedIfdsFromInfo( int info ) { int ifdFlags = getAllowedIfdFlagsFromInfo( info ); int[] ifds = IfdData.getIfds(); ArrayList<Integer> l = new ArrayList<Integer>(); for( int i = 0; i < IfdId.TYPE_IFD_COUNT; i++ ) { int flag = ( ifdFlags >> i ) & 1; if( flag == 1 ) { l.add( ifds[i] ); } } if( l.size() <= 0 ) { return null; } int[] ret = new int[l.size()]; int j = 0; for( int i : l ) { ret[j++] = i; } return ret; } /** * Reads the exif tags from a file, clearing this ExifInterface object's * existing exif tags. * * @param inFileName a string representing the filepath to jpeg file. * @param options bit flag which defines which type of tags to process, see {@link it.sephiroth.android.library.exif2.ExifInterface.Options} * @see #readExif(java.io.InputStream, int) * @throws java.io.IOException */ @SuppressWarnings( "unused" ) public void readExif( String inFileName, int options ) throws IOException { if( inFileName == null ) { throw new IllegalArgumentException( NULL_ARGUMENT_STRING ); } InputStream is = null; try { is = new BufferedInputStream( new FileInputStream( inFileName ) ); readExif( is, options ); } catch( IOException e ) { closeSilently( is ); throw e; } is.close(); } /** * Reads the exif tags from an InputStream, clearing this ExifInterface * object's existing exif tags. * <pre> * ExifInterface exif = new ExifInterface(); * exif.readExif( stream, Options.OPTION_IFD_0 | Options.OPTION_IFD_1 | Options.OPTION_IFD_EXIF ); * ... * // to request all the options use the OPTION_ALL bit mask * exif.readExif( stream, Options.OPTION_ALL ); * </pre> * * @param inStream an InputStream containing a jpeg compressed image. * @param options bit flag which defines which type of tags to process, see {@link it.sephiroth.android.library.exif2.ExifInterface.Options} * @throws java.io.IOException */ @SuppressWarnings( "unused" ) public void readExif( InputStream inStream, int options ) throws IOException { if( inStream == null ) { throw new IllegalArgumentException( NULL_ARGUMENT_STRING ); } ExifData d; try { d = new ExifReader( this ).read( inStream, options ); } catch( ExifInvalidFormatException e ) { throw new IOException( "Invalid exif format : " + e ); } mData = d; } protected static void closeSilently( Closeable c ) { if( c != null ) { try { c.close(); } catch( Throwable e ) { // ignored } } } /** * Sets the exif tags, clearing this ExifInterface object's existing exif * tags. * * @param tags a collection of exif tags to set. */ public void setExif( Collection<ExifTag> tags ) { clearExif(); setTags( tags ); } /** * Clears this ExifInterface object's existing exif tags. */ public void clearExif() { mData = new ExifData( DEFAULT_BYTE_ORDER ); } /** * Puts a collection of ExifTags into this ExifInterface objects's tags. Any * previous ExifTags with the same TID and IFDs will be removed. * * @param tags a Collection of ExifTags. * @see #setTag */ public void setTags( Collection<ExifTag> tags ) { if( null == tags ) return; for( ExifTag t : tags ) { setTag( t ); } } /** * Puts an ExifTag into this ExifInterface object's tags, removing a * previous ExifTag with the same TID and IFD. The IFD it is put into will * be the one the tag was created with in {@link #buildTag}. * * @param tag an ExifTag to put into this ExifInterface's tags. * @return the previous ExifTag with the same TID and IFD or null if none * exists. */ public ExifTag setTag( ExifTag tag ) { return mData.addTag( tag ); } @SuppressWarnings( "unused" ) public void writeExif( final String dstFilename ) throws IOException { Log.i( TAG, "writeExif: " + dstFilename ); // create a backup file File dst_file = new File( dstFilename ); File bak_file = new File( dstFilename + ".t" ); // try to delete old copy of backup // Log.d( TAG, "delete old backup file" ); bak_file.delete(); // rename dst file into backup file // Log.d( TAG, "rename dst into bak" ) // if( ! dst_file.renameTo( bak_file ) ) return; try { // Log.d( TAG, "try to write into dst" ); // writeExif( bak_file.getAbsolutePath(), dst_file.getAbsolutePath() ); // Trying to write into bak_file using dst_file as source writeExif( dst_file.getAbsolutePath(), bak_file.getAbsolutePath() ); // Now switch bak into dst // Log.d( TAG, "rename the bak into dst" ); bak_file.renameTo( dst_file ); } catch( IOException e ) { throw e; } finally { // deleting backup file bak_file.delete(); } } @SuppressWarnings( "unused" ) public void writeExif( final String srcFilename, final String dstFilename ) throws IOException { Log.i( TAG, "writeExif: " + dstFilename ); // src and dst cannot be the same if( srcFilename.equals( dstFilename ) ) return; // srcFilename is used *ONLY* to read the image uncompressed data // exif tags are not used here // 3. rename dst file into backup file FileInputStream input = new FileInputStream( srcFilename ); FileOutputStream output = new FileOutputStream( dstFilename ); int position = writeExif_internal( input, output, mData ); // 7. write the rest of the image.. FileChannel in_channel = input.getChannel(); FileChannel out_channel = output.getChannel(); in_channel.transferTo( position, in_channel.size() - position, out_channel ); output.flush(); IOUtils.closeQuietly( input ); IOUtils.closeQuietly( output ); } public void writeExif( final InputStream input, final String dstFilename ) throws IOException { Log.i( TAG, "writeExif: " + dstFilename ); // inpur is used *ONLY* to read the image uncompressed data // exif tags are not used here FileOutputStream output = new FileOutputStream( dstFilename ); writeExif_internal( input, output, mData ); // 7. write the rest of the image.. IOUtils.copy( input, output ); output.flush(); output.close(); } @SuppressWarnings( "unused" ) public void writeExif( final Bitmap input, final String dstFilename, int quality ) throws IOException { Log.i( TAG, "writeExif: " + dstFilename ); // inpur is used *ONLY* to read the image uncompressed data // exif tags are not used here ByteArrayOutputStream out = new ByteArrayOutputStream(); input.compress( Bitmap.CompressFormat.JPEG, quality, out ); ByteArrayInputStream in = new ByteArrayInputStream( out.toByteArray() ); out.close(); writeExif( in, dstFilename ); } private static int writeExif_internal( final InputStream input, final OutputStream output, ExifData exifData ) throws IOException { // Log.i( TAG, "writeExif_internal" ); // 1. read the output file first ExifInterface src_exif = new ExifInterface(); src_exif.readExif( input, 0 ); // 4. Create the destination outputstream // 5. write headers output.write( 0xFF ); output.write( JpegHeader.TAG_SOI ); final List<ExifParser.Section> sections = src_exif.mData.getSections(); // 6. write all the sections from the srcFilename if( sections.get( 0 ).type != JpegHeader.TAG_M_JFIF ) { Log.w( TAG, "first section is not a JFIF or EXIF tag" ); output.write( JpegHeader.JFIF_HEADER ); } // 6.1 write the *new* EXIF tag ExifOutputStream eo = new ExifOutputStream( src_exif ); eo.setExifData( exifData ); eo.writeExifData( output ); // 6.2 write all the sections except for the SOS ( start of scan ) for( int a = 0; a < sections.size() - 1; a++ ) { ExifParser.Section current = sections.get( a ); // Log.v( TAG, "writing section.. " + String.format( "0x%2X", current.type ) ); output.write( 0xFF ); output.write( current.type ); output.write( current.data ); } // 6.3 write the last SOS marker ExifParser.Section current = sections.get( sections.size() - 1 ); // Log.v( TAG, "writing last section.. " + String.format( "0x%2X", current.type ) ); output.write( 0xFF ); output.write( current.type ); output.write( current.data ); // return the position where the input stream should be copied return src_exif.mData.mUncompressedDataPosition; } /** * Get the exif tags in this ExifInterface object or null if none exist. * * @return a List of {@link ExifTag}s. */ public List<ExifTag> getAllTags() { return mData.getAllTags(); } /** * Reads the exif tags from a byte array, clearing this ExifInterface * object's existing exif tags. * * @param jpeg a byte array containing a jpeg compressed image. * @param options bit flag which defines which type of tags to process, see {@link it.sephiroth.android.library.exif2.ExifInterface.Options} * @throws java.io.IOException * @see #readExif(java.io.InputStream, int) */ @SuppressWarnings( "unused" ) public void readExif( byte[] jpeg, int options ) throws IOException { readExif( new ByteArrayInputStream( jpeg ), options ); } /** * Returns a list of ExifTags that share a TID (which can be obtained by * calling {@link #getTrueTagKey} on a defined tag constant) or null if none * exist. * * @param tagId a TID as defined in the exif standard (or with * {@link #defineTag}). * @return a List of {@link ExifTag}s. */ @SuppressWarnings( "unused" ) public List<ExifTag> getTagsForTagId( short tagId ) { return mData.getAllTagsForTagId( tagId ); } /** * Returns a list of ExifTags that share an IFD (which can be obtained by * calling {@link #getTrueIfd(int)} on a defined tag constant) or null if none * exist. * * @param ifdId an IFD as defined in the exif standard (or with * {@link #defineTag}). * @return a List of {@link ExifTag}s. */ @SuppressWarnings( "unused" ) public List<ExifTag> getTagsForIfdId( int ifdId ) { return mData.getAllTagsForIfd( ifdId ); } /** * Returns the ExifTag in that tag's default IFD for a defined tag constant * or null if none exists. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return an {@link ExifTag} or null if none exists. */ public ExifTag getTag( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTag( tagId, ifdId ); } /** * Gets the default IFD for a tag. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return the default IFD for a tag definition or {@link #IFD_NULL} if no * definition exists. */ public int getDefinedTagDefaultIfd( int tagId ) { int info = getTagInfo().get( tagId ); if( info == DEFINITION_NULL ) { return IFD_NULL; } return getTrueIfd( tagId ); } /** * Gets an ExifTag for an IFD other than the tag's default. * * @see #getTag */ public ExifTag getTag( int tagId, int ifdId ) { if( ! ExifTag.isValidIfd( ifdId ) ) { return null; } return mData.getTag( getTrueTagKey( tagId ), ifdId ); } protected SparseIntArray getTagInfo() { if( mTagInfo == null ) { mTagInfo = new SparseIntArray(); initTagInfo(); } return mTagInfo; } /** * Returns the default IFD for a tag constant. */ public static int getTrueIfd( int tag ) { return tag >>> 16; } /** * Returns the TID for a tag constant. */ public static short getTrueTagKey( int tag ) { // Truncate return (short) tag; } private void initTagInfo() { /** * We put tag information in a 4-bytes integer. The first byte a bitmask * representing the allowed IFDs of the tag, the second byte is the data * type, and the last two byte are a short value indicating the default * component count of this tag. */ // IFD0 tags int[] ifdAllowedIfds = { IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1 }; int ifdFlags = getFlagsFromAllowedIfds( ifdAllowedIfds ) << 24; mTagInfo.put( ExifInterface.TAG_MAKE, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_IMAGE_WIDTH, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_IMAGE_LENGTH, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_BITS_PER_SAMPLE, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 ); mTagInfo.put( ExifInterface.TAG_COMPRESSION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_PHOTOMETRIC_INTERPRETATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_ORIENTATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SAMPLES_PER_PIXEL, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_PLANAR_CONFIGURATION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_Y_CB_CR_SUB_SAMPLING, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_Y_CB_CR_POSITIONING, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_X_RESOLUTION, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_Y_RESOLUTION, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_RESOLUTION_UNIT, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_STRIP_OFFSETS, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 ); mTagInfo.put( ExifInterface.TAG_ROWS_PER_STRIP, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_STRIP_BYTE_COUNTS, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 ); mTagInfo.put( ExifInterface.TAG_TRANSFER_FUNCTION, ifdFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 3 * 256 ); mTagInfo.put( ExifInterface.TAG_WHITE_POINT, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_PRIMARY_CHROMATICITIES, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6 ); mTagInfo.put( ExifInterface.TAG_Y_CB_CR_COEFFICIENTS, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3 ); mTagInfo.put( ExifInterface.TAG_REFERENCE_BLACK_WHITE, ifdFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 6 ); mTagInfo.put( ExifInterface.TAG_DATE_TIME, ifdFlags | ExifTag.TYPE_ASCII << 16 | 20 ); mTagInfo.put( ExifInterface.TAG_IMAGE_DESCRIPTION, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_MODEL, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_SOFTWARE, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_ARTIST, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_COPYRIGHT, ifdFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_EXIF_IFD, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_IFD, ifdFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); // IFD1 tags int[] ifd1AllowedIfds = { IfdId.TYPE_IFD_1 }; int ifdFlags1 = getFlagsFromAllowedIfds( ifd1AllowedIfds ) << 24; mTagInfo.put( ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT, ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, ifdFlags1 | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); // Exif tags int[] exifAllowedIfds = { IfdId.TYPE_IFD_EXIF }; int exifFlags = getFlagsFromAllowedIfds( exifAllowedIfds ) << 24; mTagInfo.put( ExifInterface.TAG_EXIF_VERSION, exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4 ); mTagInfo.put( ExifInterface.TAG_FLASHPIX_VERSION, exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4 ); mTagInfo.put( ExifInterface.TAG_COLOR_SPACE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_COMPONENTS_CONFIGURATION, exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 4 ); mTagInfo.put( ExifInterface.TAG_COMPRESSED_BITS_PER_PIXEL, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_PIXEL_X_DIMENSION, exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_PIXEL_Y_DIMENSION, exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_MAKER_NOTE, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_USER_COMMENT, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_RELATED_SOUND_FILE, exifFlags | ExifTag.TYPE_ASCII << 16 | 13 ); mTagInfo.put( ExifInterface.TAG_DATE_TIME_ORIGINAL, exifFlags | ExifTag.TYPE_ASCII << 16 | 20 ); mTagInfo.put( ExifInterface.TAG_DATE_TIME_DIGITIZED, exifFlags | ExifTag.TYPE_ASCII << 16 | 20 ); mTagInfo.put( ExifInterface.TAG_SUB_SEC_TIME, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_SUB_SEC_TIME_ORIGINAL, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_SUB_SEC_TIME_DIGITIZED, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_IMAGE_UNIQUE_ID, exifFlags | ExifTag.TYPE_ASCII << 16 | 33 ); mTagInfo.put( ExifInterface.TAG_LENS_SPECS, exifFlags | ExifTag.TYPE_RATIONAL << 16 | 4 ); mTagInfo.put( ExifInterface.TAG_LENS_MAKE, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_LENS_MODEL, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_SENSITIVITY_TYPE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_EXPOSURE_TIME, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_F_NUMBER, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_EXPOSURE_PROGRAM, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SPECTRAL_SENSITIVITY, exifFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_ISO_SPEED_RATINGS, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 ); mTagInfo.put( ExifInterface.TAG_OECF, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_SHUTTER_SPEED_VALUE, exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_APERTURE_VALUE, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_BRIGHTNESS_VALUE, exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_EXPOSURE_BIAS_VALUE, exifFlags | ExifTag.TYPE_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_MAX_APERTURE_VALUE, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SUBJECT_DISTANCE, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_METERING_MODE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_LIGHT_SOURCE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FLASH, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FOCAL_LENGTH, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SUBJECT_AREA, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 ); mTagInfo.put( ExifInterface.TAG_FLASH_ENERGY, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SPATIAL_FREQUENCY_RESPONSE, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_FOCAL_PLANE_X_RESOLUTION, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FOCAL_PLANE_Y_RESOLUTION, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FOCAL_PLANE_RESOLUTION_UNIT, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SUBJECT_LOCATION, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_EXPOSURE_INDEX, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SENSING_METHOD, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FILE_SOURCE, exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SCENE_TYPE, exifFlags | ExifTag.TYPE_UNDEFINED << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_CFA_PATTERN, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_CUSTOM_RENDERED, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_EXPOSURE_MODE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_WHITE_BALANCE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_DIGITAL_ZOOM_RATIO, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_FOCAL_LENGTH_IN_35_MM_FILE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SCENE_CAPTURE_TYPE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GAIN_CONTROL, exifFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_CONTRAST, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SATURATION, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_SHARPNESS, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_DEVICE_SETTING_DESCRIPTION, exifFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_SUBJECT_DISTANCE_RANGE, exifFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_INTEROPERABILITY_IFD, exifFlags | ExifTag.TYPE_UNSIGNED_LONG << 16 | 1 ); // GPS tag int[] gpsAllowedIfds = { IfdId.TYPE_IFD_GPS }; int gpsFlags = getFlagsFromAllowedIfds( gpsAllowedIfds ) << 24; mTagInfo.put( ExifInterface.TAG_GPS_VERSION_ID, gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 4 ); mTagInfo.put( ExifInterface.TAG_GPS_LATITUDE_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_LONGITUDE_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_LATITUDE, gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3 ); mTagInfo.put( ExifInterface.TAG_GPS_LONGITUDE, gpsFlags | ExifTag.TYPE_RATIONAL << 16 | 3 ); mTagInfo.put( ExifInterface.TAG_GPS_ALTITUDE_REF, gpsFlags | ExifTag.TYPE_UNSIGNED_BYTE << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_ALTITUDE, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_TIME_STAMP, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 3 ); mTagInfo.put( ExifInterface.TAG_GPS_SATTELLITES, gpsFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_GPS_STATUS, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_MEASURE_MODE, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_DOP, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_SPEED_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_SPEED, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_TRACK_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_TRACK, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_IMG_DIRECTION_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_IMG_DIRECTION, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_MAP_DATUM, gpsFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_LATITUDE_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_LATITUDE, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_BEARING_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_BEARING, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_DISTANCE_REF, gpsFlags | ExifTag.TYPE_ASCII << 16 | 2 ); mTagInfo.put( ExifInterface.TAG_GPS_DEST_DISTANCE, gpsFlags | ExifTag.TYPE_UNSIGNED_RATIONAL << 16 | 1 ); mTagInfo.put( ExifInterface.TAG_GPS_PROCESSING_METHOD, gpsFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_GPS_AREA_INFORMATION, gpsFlags | ExifTag.TYPE_UNDEFINED << 16 ); mTagInfo.put( ExifInterface.TAG_GPS_DATE_STAMP, gpsFlags | ExifTag.TYPE_ASCII << 16 | 11 ); mTagInfo.put( ExifInterface.TAG_GPS_DIFFERENTIAL, gpsFlags | ExifTag.TYPE_UNSIGNED_SHORT << 16 | 11 ); // Interoperability tag int[] interopAllowedIfds = { IfdId.TYPE_IFD_INTEROPERABILITY }; int interopFlags = getFlagsFromAllowedIfds( interopAllowedIfds ) << 24; mTagInfo.put( TAG_INTEROPERABILITY_INDEX, interopFlags | ExifTag.TYPE_ASCII << 16 ); mTagInfo.put( TAG_INTEROP_VERSION, interopFlags | ExifTag.TYPE_UNDEFINED << 16 | 4 ); } protected static int getFlagsFromAllowedIfds( int[] allowedIfds ) { if( allowedIfds == null || allowedIfds.length == 0 ) { return 0; } int flags = 0; int[] ifds = IfdData.getIfds(); for( int i = 0; i < IfdId.TYPE_IFD_COUNT; i++ ) { for( int j : allowedIfds ) { if( ifds[i] == j ) { flags |= 1 << i; break; } } } return flags; } /** * Returns the value of the ExifTag in that tag's default IFD for a defined * tag constant or null if none exists or the value could not be cast into * the return type. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return the value of the ExifTag or null if none exists. */ @SuppressWarnings( "unused" ) public Object getTagValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagValue( tagId, ifdId ); } /** * Gets a tag value for an IFD other than the tag's default. * * @see #getTagValue */ public Object getTagValue( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); return ( t == null ) ? null : t.getValue(); } /** * @see #getTagValue */ public String getTagStringValue( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return null; } return t.getValueAsString(); } /** * @see #getTagValue */ public String getTagStringValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagStringValue( tagId, ifdId ); } /** * @see #getTagValue */ @SuppressWarnings( "unused" ) public Long getTagLongValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagLongValue( tagId, ifdId ); } /** * @see #getTagValue */ public Long getTagLongValue( int tagId, int ifdId ) { long[] l = getTagLongValues( tagId, ifdId ); if( l == null || l.length <= 0 ) { return null; } return new Long( l[0] ); } /** * @see #getTagValue */ public long[] getTagLongValues( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return null; } return t.getValueAsLongs(); } /** * @see #getTagValue */ public Integer getTagIntValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagIntValue( tagId, ifdId ); } /** * @see #getTagValue */ public Integer getTagIntValue( int tagId, int ifdId ) { int[] l = getTagIntValues( tagId, ifdId ); if( l == null || l.length <= 0 ) { return null; } return new Integer( l[0] ); } /** * @see #getTagValue */ public int[] getTagIntValues( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return null; } return t.getValueAsInts(); } /** * @see #getTagValue */ public Byte getTagByteValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagByteValue( tagId, ifdId ); } /** * @see #getTagValue */ public Byte getTagByteValue( int tagId, int ifdId ) { byte[] l = getTagByteValues( tagId, ifdId ); if( l == null || l.length <= 0 ) { return null; } return new Byte( l[0] ); } /** * @see #getTagValue */ public byte[] getTagByteValues( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return null; } return t.getValueAsBytes(); } /** * @see #getTagValue */ public Rational getTagRationalValue( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagRationalValue( tagId, ifdId ); } /** * @see #getTagValue */ public Rational getTagRationalValue( int tagId, int ifdId ) { Rational[] l = getTagRationalValues( tagId, ifdId ); if( l == null || l.length == 0 ) { return null; } return new Rational( l[0] ); } /* * Getter methods that are similar to getTagValue. Null is returned if the * tag value cannot be cast into the return type. */ /** * @see #getTagValue */ public Rational[] getTagRationalValues( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return null; } return t.getValueAsRationals(); } /** * @see #getTagValue */ @SuppressWarnings( "unused" ) public long[] getTagLongValues( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagLongValues( tagId, ifdId ); } /** * @see #getTagValue */ @SuppressWarnings( "unused" ) public int[] getTagIntValues( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagIntValues( tagId, ifdId ); } /** * @see #getTagValue */ @SuppressWarnings( "unused" ) public byte[] getTagByteValues( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagByteValues( tagId, ifdId ); } /** * @see #getTagValue */ public Rational[] getTagRationalValues( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return getTagRationalValues( tagId, ifdId ); } /** * Checks whether a tag has a defined number of elements. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return true if the tag has a defined number of elements. */ @SuppressWarnings( "unused" ) public boolean isTagCountDefined( int tagId ) { int info = getTagInfo().get( tagId ); // No value in info can be zero, as all tags have a non-zero type return info != 0 && getComponentCountFromInfo( info ) != ExifTag.SIZE_UNDEFINED; } protected static int getComponentCountFromInfo( int info ) { return info & 0x0ffff; } /** * Gets the defined number of elements for a tag. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return the number of elements or {@link ExifTag#SIZE_UNDEFINED} if the * tag or the number of elements is not defined. */ @SuppressWarnings( "unused" ) public int getDefinedTagCount( int tagId ) { int info = getTagInfo().get( tagId ); if( info == 0 ) { return ExifTag.SIZE_UNDEFINED; } return getComponentCountFromInfo( info ); } /** * Gets the number of elements for an ExifTag in a given IFD. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param ifdId the IFD containing the ExifTag to check. * @return the number of elements in the ExifTag, if the tag's size is * undefined this will return the actual number of elements that is * in the ExifTag's value. */ @SuppressWarnings( "unused" ) public int getActualTagCount( int tagId, int ifdId ) { ExifTag t = getTag( tagId, ifdId ); if( t == null ) { return 0; } return t.getComponentCount(); } /** * Gets the defined type for a tag. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @return the type. * @see ExifTag#getDataType() */ @SuppressWarnings( "unused" ) public short getDefinedTagType( int tagId ) { int info = getTagInfo().get( tagId ); if( info == 0 ) { return - 1; } return getTypeFromInfo( info ); } protected static short getTypeFromInfo( int info ) { return (short) ( ( info >> 16 ) & 0x0ff ); } protected ExifTag buildUninitializedTag( int tagId ) { int info = getTagInfo().get( tagId ); if( info == 0 ) { return null; } short type = getTypeFromInfo( info ); int definedCount = getComponentCountFromInfo( info ); boolean hasDefinedCount = ( definedCount != ExifTag.SIZE_UNDEFINED ); int ifdId = getTrueIfd( tagId ); return new ExifTag( getTrueTagKey( tagId ), type, definedCount, ifdId, hasDefinedCount ); } /** * Sets the value of an ExifTag if it exists it's default IFD. The value * must be the correct type and length for that ExifTag. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param val the value to set. * @return true if success, false if the ExifTag doesn't exist or the value * is the wrong type/length. */ @SuppressWarnings( "unused" ) public boolean setTagValue( int tagId, Object val ) { int ifdId = getDefinedTagDefaultIfd( tagId ); return setTagValue( tagId, ifdId, val ); } /** * Sets the value of an ExifTag if it exists in the given IFD. The value * must be the correct type and length for that ExifTag. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param ifdId the IFD that the ExifTag is in. * @param val the value to set. * @return true if success, false if the ExifTag doesn't exist or the value * is the wrong type/length. * @see #setTagValue */ public boolean setTagValue( int tagId, int ifdId, Object val ) { ExifTag t = getTag( tagId, ifdId ); return t != null && t.setValue( val ); } /** * Removes the ExifTag for a tag constant from that tag's default IFD. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. */ public void deleteTag( int tagId ) { int ifdId = getDefinedTagDefaultIfd( tagId ); deleteTag( tagId, ifdId ); } /** * Removes the ExifTag for a tag constant from the given IFD. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param ifdId the IFD of the ExifTag to remove. */ public void deleteTag( int tagId, int ifdId ) { mData.removeTag( getTrueTagKey( tagId ), ifdId ); } /** * Creates a new tag definition in this ExifInterface object for a given TID * and default IFD. Creating a definition with the same TID and default IFD * as a previous definition will override it. * * @param tagId the TID for the tag. * @param defaultIfd the default IFD for the tag. * @param tagType the type of the tag (see {@link ExifTag#getDataType()}). * @param defaultComponentCount the number of elements of this tag's type in * the tags value. * @param allowedIfds the IFD's this tag is allowed to be put in. * @return the defined tag constant (e.g. {@link #TAG_IMAGE_WIDTH}) or * {@link #TAG_NULL} if the definition could not be made. */ @SuppressWarnings( "unused" ) public int setTagDefinition( short tagId, int defaultIfd, short tagType, short defaultComponentCount, int[] allowedIfds ) { if( sBannedDefines.contains( tagId ) ) { return TAG_NULL; } if( ExifTag.isValidType( tagType ) && ExifTag.isValidIfd( defaultIfd ) ) { int tagDef = defineTag( defaultIfd, tagId ); if( tagDef == TAG_NULL ) { return TAG_NULL; } int[] otherDefs = getTagDefinitionsForTagId( tagId ); SparseIntArray infos = getTagInfo(); // Make sure defaultIfd is in allowedIfds boolean defaultCheck = false; for( int i : allowedIfds ) { if( defaultIfd == i ) { defaultCheck = true; } if( ! ExifTag.isValidIfd( i ) ) { return TAG_NULL; } } if( ! defaultCheck ) { return TAG_NULL; } int ifdFlags = getFlagsFromAllowedIfds( allowedIfds ); // Make sure no identical tags can exist in allowedIfds if( otherDefs != null ) { for( int def : otherDefs ) { int tagInfo = infos.get( def ); int allowedFlags = getAllowedIfdFlagsFromInfo( tagInfo ); if( ( ifdFlags & allowedFlags ) != 0 ) { return TAG_NULL; } } } getTagInfo().put( tagDef, ifdFlags << 24 | ( tagType << 16 ) | defaultComponentCount ); return tagDef; } return TAG_NULL; } @SuppressWarnings( "unused" ) protected int getTagDefinition( short tagId, int defaultIfd ) { return getTagInfo().get( defineTag( defaultIfd, tagId ) ); } /** * Returns the constant representing a tag with a given TID and default IFD. */ public static int defineTag( int ifdId, short tagId ) { return ( tagId & 0x0000ffff ) | ( ifdId << 16 ); } protected int[] getTagDefinitionsForTagId( short tagId ) { int[] ifds = IfdData.getIfds(); int[] defs = new int[ifds.length]; int counter = 0; SparseIntArray infos = getTagInfo(); for( int i : ifds ) { int def = defineTag( i, tagId ); if( infos.get( def ) != DEFINITION_NULL ) { defs[counter++] = def; } } if( counter == 0 ) { return null; } return Arrays.copyOfRange( defs, 0, counter ); } @SuppressWarnings( "unused" ) protected int getTagDefinitionForTag( ExifTag tag ) { short type = tag.getDataType(); int count = tag.getComponentCount(); int ifd = tag.getIfd(); return getTagDefinitionForTag( tag.getTagId(), type, count, ifd ); } protected int getTagDefinitionForTag( short tagId, short type, int count, int ifd ) { int[] defs = getTagDefinitionsForTagId( tagId ); if( defs == null ) { return TAG_NULL; } SparseIntArray infos = getTagInfo(); int ret = TAG_NULL; for( int i : defs ) { int info = infos.get( i ); short def_type = getTypeFromInfo( info ); int def_count = getComponentCountFromInfo( info ); int[] def_ifds = getAllowedIfdsFromInfo( info ); boolean valid_ifd = false; for( int j : def_ifds ) { if( j == ifd ) { valid_ifd = true; break; } } if( valid_ifd && type == def_type && ( count == def_count || def_count == ExifTag.SIZE_UNDEFINED ) ) { ret = i; break; } } return ret; } /** * Removes a tag definition for given defined tag constant. * * @param tagId a defined tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. */ @SuppressWarnings( "unused" ) public void removeTagDefinition( int tagId ) { getTagInfo().delete( tagId ); } /** * Resets tag definitions to the default ones. */ @SuppressWarnings( "unused" ) public void resetTagDefinitions() { mTagInfo = null; } /** * Returns the thumbnail from IFD1 as a bitmap, or null if none exists. * * @return the thumbnail as a bitmap. */ public Bitmap getThumbnailBitmap() { if( mData.hasCompressedThumbnail() ) { byte[] thumb = mData.getCompressedThumbnail(); return BitmapFactory.decodeByteArray( thumb, 0, thumb.length ); } else if( mData.hasUncompressedStrip() ) { // TODO: implement uncompressed } return null; } /** * Returns the thumbnail from IFD1 as a byte array, or null if none exists. * The bytes may either be an uncompressed strip as specified in the exif * standard or a jpeg compressed image. * * @return the thumbnail as a byte array. */ @SuppressWarnings( "unused" ) public byte[] getThumbnailBytes() { if( mData.hasCompressedThumbnail() ) { return mData.getCompressedThumbnail(); } else if( mData.hasUncompressedStrip() ) { // TODO: implement this } return null; } /** * Returns the thumbnail if it is jpeg compressed, or null if none exists. * * @return the thumbnail as a byte array. */ public byte[] getThumbnail() { return mData.getCompressedThumbnail(); } /** * Returns the JPEG quality used to generate the image * or 0 if not found * * @return */ public int getQualityGuess() { return mData.getQualityGuess(); } /** * this gives information about the process used to create the JPEG file. * Possible values are: * <ul> * <li>'0' Unknown</li> * <li>'192' Baseline</li> * <li>'193' Extended sequential</li> * <li>'194' Progressive</li> * <li>'195' Lossless</li> * <li>'197' Differential sequential</li> * <li>'198' Differential progressive</li> * <li>'199' Differential lossless</li> * <li>'201' Extended sequential, arithmetic coding</li> * <li>'202' Progressive, arithmetic coding</li> * <li>'203' Lossless, arithmetic coding</li> * <li>'205' Differential sequential, arithmetic coding</li> * <li>'206' Differential progressive, arithmetic codng</li> * <li>'207' Differential lossless, arithmetic coding</li> * </ul> */ public short getJpegProcess() { return mData.getJpegProcess(); } /** * Returns the Image size as decoded from the SOF marker */ public int[] getImageSize() { return mData.getImageSize(); } /** * Check if thumbnail is compressed. * * @return true if the thumbnail is compressed. */ @SuppressWarnings( "unused" ) public boolean isThumbnailCompressed() { return mData.hasCompressedThumbnail(); } /** * Check if thumbnail exists. * * @return true if a compressed thumbnail exists. */ public boolean hasThumbnail() { // TODO: add back in uncompressed strip return mData.hasCompressedThumbnail(); } /** * Sets the thumbnail to be a jpeg compressed bitmap. Clears any prior * thumbnail. * * @param thumb a bitmap to compress to a jpeg thumbnail. * @return true if the thumbnail was set. */ @SuppressWarnings( "unused" ) public boolean setCompressedThumbnail( Bitmap thumb ) { ByteArrayOutputStream thumbnail = new ByteArrayOutputStream(); if( ! thumb.compress( Bitmap.CompressFormat.JPEG, 90, thumbnail ) ) { return false; } return setCompressedThumbnail( thumbnail.toByteArray() ); } /** * Sets the thumbnail to be a jpeg compressed image. Clears any prior * thumbnail. * * @param thumb a byte array containing a jpeg compressed image. * @return true if the thumbnail was set. */ public boolean setCompressedThumbnail( byte[] thumb ) { mData.clearThumbnailAndStrips(); mData.setCompressedThumbnail( thumb ); return true; } /** * Clears the compressed thumbnail if it exists. */ @SuppressWarnings( "unused" ) public void removeCompressedThumbnail() { mData.setCompressedThumbnail( null ); } /** * Decodes the user comment tag into string as specified in the EXIF * standard. Returns null if decoding failed. */ @SuppressWarnings( "unused" ) public String getUserComment() { return mData.getUserComment(); } /** * Return the altitude in meters. If the exif tag does not exist, return * <var>defaultValue</var>. * * @param defaultValue the value to return if the tag is not available. */ @SuppressWarnings( "unused" ) public double getAltitude( double defaultValue ) { Byte ref = getTagByteValue( TAG_GPS_ALTITUDE_REF ); Rational gpsAltitude = getTagRationalValue( TAG_GPS_ALTITUDE ); int seaLevel = 1; if( null != ref ) { seaLevel = ref.intValue() == 1 ? - 1 : 1; } if( gpsAltitude != null ) { return gpsAltitude.toDouble() * seaLevel; } return defaultValue; } /** * Gets the GPS latitude and longitude as a pair of doubles from this * ExifInterface object's tags, or null if the necessary tags do not exist. * * @return an array of 2 doubles containing the latitude, and longitude * respectively. * @see #convertLatOrLongToDouble */ public double[] getLatLongAsDoubles() { Rational[] latitude = getTagRationalValues( TAG_GPS_LATITUDE ); String latitudeRef = getTagStringValue( TAG_GPS_LATITUDE_REF ); Rational[] longitude = getTagRationalValues( TAG_GPS_LONGITUDE ); String longitudeRef = getTagStringValue( TAG_GPS_LONGITUDE_REF ); if( latitude == null || longitude == null || latitudeRef == null || longitudeRef == null || latitude.length < 3 || longitude.length < 3 ) { return null; } double[] latLon = new double[2]; latLon[0] = convertLatOrLongToDouble( latitude, latitudeRef ); latLon[1] = convertLatOrLongToDouble( longitude, longitudeRef ); return latLon; } /** * Returns a formatted String with the latitude representation:<br /> * 39° 8' 16.8" N */ public String getLatitude() { Rational[] latitude = getTagRationalValues( TAG_GPS_LATITUDE ); String latitudeRef = getTagStringValue( TAG_GPS_LATITUDE_REF ); if( null == latitude || null == latitudeRef ) return null; return convertRationalLatLonToString( latitude, latitudeRef ); } /** * Returns a formatted String with the longitude representation:<br /> * 77° 37' 51.6" W */ public String getLongitude() { Rational[] longitude = getTagRationalValues( TAG_GPS_LONGITUDE ); String longitudeRef = getTagStringValue( TAG_GPS_LONGITUDE_REF ); if( null == longitude || null == longitudeRef ) return null; return convertRationalLatLonToString( longitude, longitudeRef ); } private static String convertRationalLatLonToString( Rational[] coord, String ref ) { try { double degrees = coord[0].toDouble(); double minutes = coord[1].toDouble(); double seconds = coord[2].toDouble(); ref = ref.substring( 0, 1 ); return String.format( "%1$.0f° %2$.0f' %3$.0f\" %4$s", degrees, minutes, seconds, ref.toUpperCase( Locale.getDefault() ) ); } catch( NumberFormatException e ) { e.printStackTrace(); } catch( ArrayIndexOutOfBoundsException e ) { e.printStackTrace(); } return null; } /** * Given an exif date time, like {@link #TAG_DATE_TIME} or {@link #TAG_DATE_TIME_DIGITIZED} * returns a java Date object * * @param dateTimeString one of the value of {@link #TAG_DATE_TIME} or {@link #TAG_DATE_TIME_DIGITIZED} * @param timeZone the target timezone * @return the parsed date */ public static Date getDateTime( String dateTimeString, TimeZone timeZone ) { if( dateTimeString == null ) return null; DateFormat formatter = new SimpleDateFormat( DATETIME_FORMAT_STR ); formatter.setTimeZone( timeZone ); try { return formatter.parse( dateTimeString ); } catch( IllegalArgumentException e ) { e.printStackTrace(); } catch( ParseException e ) { e.printStackTrace(); } return null; } /** * Creates, formats, and sets the DateTimeStamp tag for one of: * {@link #TAG_DATE_TIME}, {@link #TAG_DATE_TIME_DIGITIZED}, * {@link #TAG_DATE_TIME_ORIGINAL}. * * @param tagId one of the DateTimeStamp tags. * @param timestamp a timestamp to format. * @param timezone a TimeZone object. * @return true if success, false if the tag could not be set. */ public boolean addDateTimeStampTag( int tagId, long timestamp, TimeZone timezone ) { if( tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED || tagId == TAG_DATE_TIME_ORIGINAL ) { mDateTimeStampFormat.setTimeZone( timezone ); ExifTag t = buildTag( tagId, mDateTimeStampFormat.format( timestamp ) ); if( t == null ) { return false; } setTag( t ); } else { return false; } return true; } /** * Creates a tag for a defined tag constant in the tag's default IFD. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param val the tag's value. * @return an ExifTag object. */ public ExifTag buildTag( int tagId, Object val ) { int ifdId = getTrueIfd( tagId ); return buildTag( tagId, ifdId, val ); } /** * Creates a tag for a defined tag constant in a given IFD if that IFD is * allowed for the tag. This method will fail anytime the appropriate * {@link ExifTag#setValue} for this tag's datatype would fail. * * @param tagId a tag constant, e.g. {@link #TAG_IMAGE_WIDTH}. * @param ifdId the IFD that the tag should be in. * @param val the value of the tag to set. * @return an ExifTag object or null if one could not be constructed. * @see #buildTag */ public ExifTag buildTag( int tagId, int ifdId, Object val ) { int info = getTagInfo().get( tagId ); if( info == 0 || val == null ) { return null; } short type = getTypeFromInfo( info ); int definedCount = getComponentCountFromInfo( info ); boolean hasDefinedCount = ( definedCount != ExifTag.SIZE_UNDEFINED ); if( ! ExifInterface.isIfdAllowed( info, ifdId ) ) { return null; } ExifTag t = new ExifTag( getTrueTagKey( tagId ), type, definedCount, ifdId, hasDefinedCount ); if( ! t.setValue( val ) ) { return null; } return t; } protected static boolean isIfdAllowed( int info, int ifd ) { int[] ifds = IfdData.getIfds(); int ifdFlags = getAllowedIfdFlagsFromInfo( info ); for( int i = 0; i < ifds.length; i++ ) { if( ifd == ifds[i] && ( ( ifdFlags >> i ) & 1 ) == 1 ) { return true; } } return false; } protected static int getAllowedIfdFlagsFromInfo( int info ) { return info >>> 24; } /** * Creates and sets all to the GPS tags for a give latitude and longitude. * * @param latitude a GPS latitude coordinate. * @param longitude a GPS longitude coordinate. * @return true if success, false if they could not be created or set. */ @SuppressWarnings( "unused" ) public boolean addGpsTags( double latitude, double longitude ) { ExifTag latTag = buildTag( TAG_GPS_LATITUDE, toExifLatLong( latitude ) ); ExifTag longTag = buildTag( TAG_GPS_LONGITUDE, toExifLatLong( longitude ) ); ExifTag latRefTag = buildTag( TAG_GPS_LATITUDE_REF, latitude >= 0 ? GpsLatitudeRef.NORTH : GpsLatitudeRef.SOUTH ); ExifTag longRefTag = buildTag( TAG_GPS_LONGITUDE_REF, longitude >= 0 ? GpsLongitudeRef.EAST : GpsLongitudeRef.WEST ); if( latTag == null || longTag == null || latRefTag == null || longRefTag == null ) { return false; } setTag( latTag ); setTag( longTag ); setTag( latRefTag ); setTag( longRefTag ); return true; } private static Rational[] toExifLatLong( double value ) { // convert to the format dd/1 mm/1 ssss/100 value = Math.abs( value ); int degrees = (int) value; value = ( value - degrees ) * 60; int minutes = (int) value; value = ( value - minutes ) * 6000; int seconds = (int) value; return new Rational[]{ new Rational( degrees, 1 ), new Rational( minutes, 1 ), new Rational( seconds, 100 ) }; } /** * Creates and sets the GPS timestamp tag. * * @param timestamp a GPS timestamp. * @return true if success, false if could not be created or set. */ @SuppressWarnings( "unused" ) public boolean addGpsDateTimeStampTag( long timestamp ) { ExifTag t = buildTag( TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format( timestamp ) ); if( t == null ) { return false; } setTag( t ); mGPSTimeStampCalendar.setTimeInMillis( timestamp ); t = buildTag( TAG_GPS_TIME_STAMP, new Rational[]{ new Rational( mGPSTimeStampCalendar.get( Calendar.HOUR_OF_DAY ), 1 ), new Rational( mGPSTimeStampCalendar.get( Calendar.MINUTE ), 1 ), new Rational( mGPSTimeStampCalendar.get( Calendar.SECOND ), 1 ) } ); if( t == null ) { return false; } setTag( t ); return true; } /** * Return the aperture size, if present, 0 if missing */ public double getApertureSize() { Rational rational = getTagRationalValue( TAG_F_NUMBER ); if( null != rational && rational.toDouble() > 0 ) { return rational.toDouble(); } rational = getTagRationalValue( TAG_APERTURE_VALUE ); if( null != rational && rational.toDouble() > 0 ) { return Math.exp( rational.toDouble() * Math.log( 2 ) * 0.5 ); } return 0; } /** * Returns the lens model as string if any of the tags {@link #TAG_LENS_MODEL} * or {@link #TAG_LENS_SPECS} are found * * @return the string representation of the lens spec */ public String getLensModelDescription() { String lensModel = getTagStringValue( TAG_LENS_MODEL ); if( null != lensModel ) return lensModel; Rational[] rat = getTagRationalValues( TAG_LENS_SPECS ); if( null != rat ) return ExifUtil.processLensSpecifications( rat ); return null; } /** * Constants for {@link #TAG_ORIENTATION}. They can be interpreted as * follows: * <ul> * <li>TOP_LEFT is the normal orientation.</li> * <li>TOP_RIGHT is a left-right mirror.</li> * <li>BOTTOM_LEFT is a 180 degree rotation.</li> * <li>BOTTOM_RIGHT is a top-bottom mirror.</li> * <li>LEFT_TOP is mirrored about the top-left<->bottom-right axis.</li> * <li>RIGHT_TOP is a 90 degree clockwise rotation.</li> * <li>LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.</li> * <li>RIGHT_BOTTOM is a 270 degree clockwise rotation.</li> * </ul> */ @SuppressWarnings( "unused" ) public static interface Orientation { public static final short TOP_LEFT = 1; public static final short TOP_RIGHT = 2; public static final short BOTTOM_RIGHT = 3; public static final short BOTTOM_LEFT = 4; public static final short LEFT_TOP = 5; public static final short RIGHT_TOP = 6; public static final short RIGHT_BOTTOM = 7; public static final short LEFT_BOTTOM = 8; } /** * Constants for {@link #TAG_Y_CB_CR_POSITIONING} */ @SuppressWarnings( "unused" ) public static interface YCbCrPositioning { public static final short CENTERED = 1; public static final short CO_SITED = 2; } /** * Constants for {@link #TAG_COMPRESSION} */ @SuppressWarnings( "unused" ) public static interface Compression { public static final short UNCOMPRESSION = 1; public static final short JPEG = 6; } // TODO: uncompressed thumbnail setters /** * Constants for {@link #TAG_RESOLUTION_UNIT} */ @SuppressWarnings( "unused" ) public static interface ResolutionUnit { public static final short INCHES = 2; public static final short CENTIMETERS = 3; public static final short MILLIMETERS = 4; public static final short MICROMETERS = 5; } /** * Constants for {@link #TAG_PHOTOMETRIC_INTERPRETATION} */ @SuppressWarnings( "unused" ) public static interface PhotometricInterpretation { public static final short RGB = 2; public static final short YCBCR = 6; } /** * Constants for {@link #TAG_PLANAR_CONFIGURATION} */ @SuppressWarnings( "unused" ) public static interface PlanarConfiguration { public static final short CHUNKY = 1; public static final short PLANAR = 2; } // Convenience methods: /** * Constants for {@link #TAG_EXPOSURE_PROGRAM} */ @SuppressWarnings( "unused" ) public static interface ExposureProgram { public static final short NOT_DEFINED = 0; public static final short MANUAL = 1; public static final short NORMAL_PROGRAM = 2; public static final short APERTURE_PRIORITY = 3; public static final short SHUTTER_PRIORITY = 4; public static final short CREATIVE_PROGRAM = 5; public static final short ACTION_PROGRAM = 6; public static final short PROTRAIT_MODE = 7; public static final short LANDSCAPE_MODE = 8; } /** * Constants for {@link #TAG_METERING_MODE} */ @SuppressWarnings( "unused" ) public static interface MeteringMode { public static final short UNKNOWN = 0; public static final short AVERAGE = 1; public static final short CENTER_WEIGHTED_AVERAGE = 2; public static final short SPOT = 3; public static final short MULTISPOT = 4; public static final short PATTERN = 5; public static final short PARTAIL = 6; public static final short OTHER = 255; } @SuppressWarnings( "unused" ) public static byte[] toBitArray( short value ) { byte[] result = new byte[16]; for( int i = 0; i < 16; i++ ) { result[15 - i] = (byte) ( ( value >> i ) & 1 ); } return result; } /** * Constants for {@link #TAG_FLASH} As the definition in Jeita EXIF 2.2 */ @SuppressWarnings( "unused" ) public static interface Flash { /** first bit */ public static enum FlashFired { NO, YES } /** Values for bits 1 and 2 indicating the status of returned light */ public static enum StrobeLightDetection { NO_DETECTION, RESERVED, LIGHT_NOT_DETECTED, LIGHT_DETECTED } /** Values for bits 3 and 4 indicating the camera's flash mode */ public static enum CompulsoryMode { UNKNOWN, FIRING, SUPPRESSION, AUTO } /** Values for bit 5 indicating the presence of a flash function. */ public static enum FlashFunction { FUNCTION_PRESENT, FUNCTION_NOR_PRESENT } /** Values for bit 6 indicating the camera's red-eye mode. */ public static enum RedEyeMode { NONE, SUPPORTED } } /** * Constants for {@link #TAG_COLOR_SPACE} */ @SuppressWarnings( "unused" ) public static interface ColorSpace { public static final short SRGB = 1; public static final short UNCALIBRATED = (short) 0xFFFF; } /** * Constants for {@link #TAG_EXPOSURE_MODE} */ @SuppressWarnings( "unused" ) public static interface ExposureMode { public static final short AUTO_EXPOSURE = 0; public static final short MANUAL_EXPOSURE = 1; public static final short AUTO_BRACKET = 2; } /** * Constants for {@link #TAG_WHITE_BALANCE} */ @SuppressWarnings( "unused" ) public static interface WhiteBalance { public static final short AUTO = 0; public static final short MANUAL = 1; } /** * Constants for {@link #TAG_SCENE_CAPTURE_TYPE} */ @SuppressWarnings( "unused" ) public static interface SceneCapture { public static final short STANDARD = 0; public static final short LANDSCAPE = 1; public static final short PROTRAIT = 2; public static final short NIGHT_SCENE = 3; } /** * Constants for {@link #TAG_COMPONENTS_CONFIGURATION} */ @SuppressWarnings( "unused" ) public static interface ComponentsConfiguration { public static final short NOT_EXIST = 0; public static final short Y = 1; public static final short CB = 2; public static final short CR = 3; public static final short R = 4; public static final short G = 5; public static final short B = 6; } /** * Constants for {@link #TAG_LIGHT_SOURCE} */ @SuppressWarnings( "unused" ) public static interface LightSource { public static final short UNKNOWN = 0; public static final short DAYLIGHT = 1; public static final short FLUORESCENT = 2; public static final short TUNGSTEN = 3; public static final short FLASH = 4; public static final short FINE_WEATHER = 9; public static final short CLOUDY_WEATHER = 10; public static final short SHADE = 11; public static final short DAYLIGHT_FLUORESCENT = 12; public static final short DAY_WHITE_FLUORESCENT = 13; public static final short COOL_WHITE_FLUORESCENT = 14; public static final short WHITE_FLUORESCENT = 15; public static final short STANDARD_LIGHT_A = 17; public static final short STANDARD_LIGHT_B = 18; public static final short STANDARD_LIGHT_C = 19; public static final short D55 = 20; public static final short D65 = 21; public static final short D75 = 22; public static final short D50 = 23; public static final short ISO_STUDIO_TUNGSTEN = 24; public static final short OTHER = 255; } /** * Constants for {@link #TAG_SENSING_METHOD} */ @SuppressWarnings( "unused" ) public static interface SensingMethod { public static final short NOT_DEFINED = 1; public static final short ONE_CHIP_COLOR = 2; public static final short TWO_CHIP_COLOR = 3; public static final short THREE_CHIP_COLOR = 4; public static final short COLOR_SEQUENTIAL_AREA = 5; public static final short TRILINEAR = 7; public static final short COLOR_SEQUENTIAL_LINEAR = 8; } /** * Constants for {@link #TAG_FILE_SOURCE} */ @SuppressWarnings( "unused" ) public static interface FileSource { public static final short DSC = 3; } /** * Constants for {@link #TAG_SCENE_TYPE} */ @SuppressWarnings( "unused" ) public static interface SceneType { public static final short DIRECT_PHOTOGRAPHED = 1; } /** * Constants for {@link #TAG_GAIN_CONTROL} */ @SuppressWarnings( "unused" ) public static interface GainControl { public static final short NONE = 0; public static final short LOW_UP = 1; public static final short HIGH_UP = 2; public static final short LOW_DOWN = 3; public static final short HIGH_DOWN = 4; } /** * Constants for {@link #TAG_CONTRAST} */ @SuppressWarnings( "unused" ) public static interface Contrast { public static final short NORMAL = 0; public static final short SOFT = 1; public static final short HARD = 2; } /** * Constants for {@link #TAG_SATURATION} */ @SuppressWarnings( "unused" ) public static interface Saturation { public static final short NORMAL = 0; public static final short LOW = 1; public static final short HIGH = 2; } /** * Constants for {@link #TAG_SHARPNESS} */ @SuppressWarnings( "unused" ) public static interface Sharpness { public static final short NORMAL = 0; public static final short SOFT = 1; public static final short HARD = 2; } /** * Constants for {@link #TAG_SUBJECT_DISTANCE} */ @SuppressWarnings( "unused" ) public static interface SubjectDistance { public static final short UNKNOWN = 0; public static final short MACRO = 1; public static final short CLOSE_VIEW = 2; public static final short DISTANT_VIEW = 3; } /** * Constants for {@link #TAG_GPS_LATITUDE_REF}, * {@link #TAG_GPS_DEST_LATITUDE_REF} */ @SuppressWarnings( "unused" ) public static interface GpsLatitudeRef { public static final String NORTH = "N"; public static final String SOUTH = "S"; } /** * Constants for {@link #TAG_GPS_LONGITUDE_REF}, * {@link #TAG_GPS_DEST_LONGITUDE_REF} */ @SuppressWarnings( "unused" ) public static interface GpsLongitudeRef { public static final String EAST = "E"; public static final String WEST = "W"; } /** * Constants for {@link #TAG_GPS_ALTITUDE_REF} */ @SuppressWarnings( "unused" ) public static interface GpsAltitudeRef { public static final short SEA_LEVEL = 0; public static final short SEA_LEVEL_NEGATIVE = 1; } /** * Constants for {@link #TAG_GPS_STATUS} */ @SuppressWarnings( "unused" ) public static interface GpsStatus { public static final String IN_PROGRESS = "A"; public static final String INTEROPERABILITY = "V"; } /** * Constants for {@link #TAG_GPS_MEASURE_MODE} */ @SuppressWarnings( "unused" ) public static interface GpsMeasureMode { public static final String MODE_2_DIMENSIONAL = "2"; public static final String MODE_3_DIMENSIONAL = "3"; } /** * Constants for {@link #TAG_GPS_SPEED_REF}, * {@link #TAG_GPS_DEST_DISTANCE_REF} */ @SuppressWarnings( "unused" ) public static interface GpsSpeedRef { public static final String KILOMETERS = "K"; public static final String MILES = "M"; public static final String KNOTS = "N"; } /** * Constants for {@link #TAG_GPS_TRACK_REF}, * {@link #TAG_GPS_IMG_DIRECTION_REF}, {@link #TAG_GPS_DEST_BEARING_REF} */ @SuppressWarnings( "unused" ) public static interface GpsTrackRef { public static final String TRUE_DIRECTION = "T"; public static final String MAGNETIC_DIRECTION = "M"; } /** * Constants for {@link #TAG_GPS_DIFFERENTIAL} */ @SuppressWarnings( "unused" ) public static interface GpsDifferential { public static final short WITHOUT_DIFFERENTIAL_CORRECTION = 0; public static final short DIFFERENTIAL_CORRECTION_APPLIED = 1; } /** * Constants for the jpeg process algorithm used. * * @see #getJpegProcess() */ @SuppressWarnings( "unused" ) public static interface JpegProcess { public static final short BASELINE = (short) 0xFFC0; public static final short EXTENDED_SEQUENTIAL = (short) 0xFFC1; public static final short PROGRESSIVE = (short) 0xFFC2; public static final short LOSSLESS = (short) 0xFFC3; public static final short DIFFERENTIAL_SEQUENTIAL = (short) 0xFFC5; public static final short DIFFERENTIAL_PROGRESSIVE = (short) 0xFFC6; public static final short DIFFERENTIAL_LOSSLESS = (short) 0xFFC7; public static final short EXTENDED_SEQ_ARITHMETIC_CODING = (short) 0xFFC9; public static final short PROGRESSIVE_AIRTHMETIC_CODING = (short) 0xFFCA; public static final short LOSSLESS_AITHMETIC_CODING = (short) 0xFFCB; public static final short DIFFERENTIAL_SEQ_ARITHMETIC_CODING = (short) 0xFFCD; public static final short DIFFERENTIAL_PROGRESSIVE_ARITHMETIC_CODING = (short) 0xFFCE; public static final short DIFFERENTIAL_LOSSLESS_ARITHMETIC_CODING = (short) 0xFFCF; } /** * Constants for the {@link #TAG_SENSITIVITY_TYPE} tag */ @SuppressWarnings( "unused" ) public static interface SensitivityType { public static final short UNKNOWN = 0; /** Standard output sensitivity */ public static final short SOS = 1; /** Recommended exposure index */ public static final short REI = 2; /** ISO Speed */ public static final short ISO = 3; /** Standard output sensitivity and Recommended output index */ public static final short SOS_REI = 4; /** Standard output sensitivity and ISO speed */ public static final short SOS_ISO = 5; /** Recommended output index and ISO Speed */ public static final short REI_ISO = 6; /** Standard output sensitivity and Recommended output index and ISO Speed */ public static final short SOS_REI_ISO = 7; } /** * Options for calling {@link #readExif(java.io.InputStream, int)}, {@link #readExif(byte[], int)}, * {@link #readExif(String, int)} */ public static interface Options { /** * Option bit to request to parse IFD0. */ int OPTION_IFD_0 = 1; /** * Option bit to request to parse IFD1. */ int OPTION_IFD_1 = 1 << 1; /** * Option bit to request to parse Exif-IFD. */ int OPTION_IFD_EXIF = 1 << 2; /** * Option bit to request to parse GPS-IFD. */ int OPTION_IFD_GPS = 1 << 3; /** * Option bit to request to parse Interoperability-IFD. */ int OPTION_IFD_INTEROPERABILITY = 1 << 4; /** * Option bit to request to parse thumbnail. */ int OPTION_THUMBNAIL = 1 << 5; /** * Option bit to request all the options */ int OPTION_ALL = OPTION_IFD_0 ^ OPTION_IFD_1 ^ OPTION_IFD_EXIF ^ OPTION_IFD_GPS ^ OPTION_IFD_INTEROPERABILITY ^ OPTION_THUMBNAIL; } }