SubwayTooter-Android-App/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInterface.kt

2615 lines
83 KiB
Kotlin

/*
* 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.content.Context
import android.graphics.Bitmap
import android.os.Build
import android.util.Log
import android.util.SparseIntArray
import org.apache.commons.io.IOUtils
import java.io.*
import java.nio.ByteOrder
import java.text.SimpleDateFormat
import java.util.*
import kotlin.math.abs
import kotlin.math.exp
import kotlin.math.ln
/**
* 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.
*
*
* 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
*/
@Suppress("unused")
class ExifInterface {
private val mGPSTimeStampCalendar : Calendar by lazy {
Calendar.getInstance(TimeZone.getTimeZone("UTC"))
}
val tagInfo : SparseIntArray by lazy {
SparseIntArray().initTagInfo()
}
private var mData = ExifData()
/**
* Get the exif tags in this ExifInterface object or null if none exist.
*
* @return a List of [ExifTag]s.
*/
val allTags : List<ExifTag>
get() = mData.allTags
/**
* Returns the JPEG quality used to generate the image
* or 0 if not found
*
* @return qualityGuess
*/
val qualityGuess : Int
get() = mData.qualityGuess
/**
* 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.
*/
val thumbnailBytes : ByteArray?
get() = mData.thumbnailBytes
/**
* Returns the thumbnail from IFD1 as a bitmap, or null if none exists.
*
* @return the thumbnail as a bitmap.
*/
val thumbnailBitmap : Bitmap?
get() = mData.thumbnailBitmap
/**
* this gives information about the process used to create the JPEG file.
* Possible values are:
*
* * '0' Unknown
* * '192' Baseline
* * '193' Extended sequential
* * '194' Progressive
* * '195' Lossless
* * '197' Differential sequential
* * '198' Differential progressive
* * '199' Differential lossless
* * '201' Extended sequential, arithmetic coding
* * '202' Progressive, arithmetic coding
* * '203' Lossless, arithmetic coding
* * '205' Differential sequential, arithmetic coding
* * '206' Differential progressive, arithmetic codng
* * '207' Differential lossless, arithmetic coding
*
*/
val jpegProcess : Short
get() = mData.jpegProcess
/**
* Returns the Image size as decoded from the SOF marker
*/
val imageSize : IntArray
get() = mData.imageSize
/**
* Check if thumbnail is compressed.
*
* @return true if the thumbnail is compressed.
*/
val isThumbnailCompressed : Boolean
get() = mData.compressedThumbnail != null
/**
* Decodes the user comment tag into string as specified in the EXIF
* standard. Returns null if decoding failed.
*/
val userComment : String?
get() = mData.userComment
/**
* 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
*/
val latLongAsDoubles : DoubleArray?
get() {
val latitude = getTagRationalValues(TAG_GPS_LATITUDE)
val latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF)
val longitude = getTagRationalValues(TAG_GPS_LONGITUDE)
val longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF)
return when {
latitude == null
|| longitude == null
|| latitudeRef == null
|| longitudeRef == null
|| latitude.size < 3
|| longitude.size < 3 -> null
else -> doubleArrayOf(
convertLatOrLongToDouble(latitude, latitudeRef),
convertLatOrLongToDouble(longitude, longitudeRef)
)
}
}
/**
* Returns a formatted String with the latitude representation:<br></br>
* 39° 8' 16.8" N
*/
val latitude : String?
get() {
val latitude = getTagRationalValues(TAG_GPS_LATITUDE)
val latitudeRef = getTagStringValue(TAG_GPS_LATITUDE_REF)
return when {
null == latitude || null == latitudeRef -> null
else -> convertRationalLatLonToString(latitude, latitudeRef)
}
}
/**
* Returns a formatted String with the longitude representation:<br></br>
* 77° 37' 51.6" W
*/
val longitude : String?
get() {
val longitude = getTagRationalValues(TAG_GPS_LONGITUDE)
val longitudeRef = getTagStringValue(TAG_GPS_LONGITUDE_REF)
return when {
null == longitude || null == longitudeRef -> null
else -> convertRationalLatLonToString(longitude, longitudeRef)
}
}
val altitude : Double? // may null
get() = when(val gpsAltitude = getTagRationalValue(TAG_GPS_ALTITUDE)) {
null -> null
else -> {
val seaLevel = when(getTagByteValue(TAG_GPS_ALTITUDE_REF)) {
1.toByte() -> - 1
else -> 1
}
gpsAltitude.toDouble() * seaLevel
}
}
/**
* Return the aperture size, if present, 0 if missing
*/
val apertureSize : Double
get() {
var v = getTagRationalValue(TAG_F_NUMBER)?.toDouble()
if(v != null && v > 0.0) return v
v = getTagRationalValue(TAG_APERTURE_VALUE)?.toDouble()
if(v != null && v > 0.0) return exp(v * ln(2.0) * 0.5)
return 0.0
}
/**
* Returns the lens model as string if any of the tags [.TAG_LENS_MODEL]
* or [.TAG_LENS_SPECS] are found
*
* @return the string representation of the lens spec
*/
val lensModelDescription : String?
get() {
val lensModel = getTagStringValue(TAG_LENS_MODEL)
if(null != lensModel) return lensModel
val rat = getTagRationalValues(TAG_LENS_SPECS)
if(null != rat) return ExifUtil.processLensSpecifications(rat)
return null
}
/**
* Given the value from [.TAG_FOCAL_PLANE_RESOLUTION_UNIT] or [.TAG_RESOLUTION_UNIT]
* this method will return the corresponding value in millimeters
*
* @param resolution [.TAG_FOCAL_PLANE_RESOLUTION_UNIT] or [.TAG_RESOLUTION_UNIT]
* @return resolution in millimeters
*/
fun getResolutionUnit(resolution : Int) : Double =
when(resolution.toShort()) {
ResolutionUnit.INCHES -> 25.4
ResolutionUnit.CENTIMETERS -> 10.0
ResolutionUnit.MILLIMETERS -> 1.0
ResolutionUnit.MICROMETERS -> .001
else -> 25.4
}
/**
* Clears this ExifInterface object's existing exif tags.
*/
private fun clearExif() : ExifInterface {
mData = ExifData()
return this
}
/**
* 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 [it.sephiroth.android.library.exif2.ExifInterface.Options]
* @throws java.io.IOException for I/O error
*/
@Throws(IOException::class)
fun readExif(inStream : InputStream, options : Int) : ExifInterface {
mData = ExifReader(this).read(inStream, options)
return this
}
/**
* 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 [it.sephiroth.android.library.exif2.ExifInterface.Options]
* @throws java.io.IOException for I/O error
* @see .readExif
*/
@Throws(IOException::class)
fun readExif(inFileName : String, options : Int) : ExifInterface =
BufferedInputStream(FileInputStream(inFileName)).use { readExif(it, options) }
/**
* 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 [it.sephiroth.android.library.exif2.ExifInterface.Options]
* @throws java.io.IOException for I/O error
* @see .readExif
*/
@Throws(IOException::class)
fun readExif(jpeg : ByteArray, options : Int) =
ByteArrayInputStream(jpeg).use { readExif(it, options) }
/**
* Sets the exif tags, clearing this ExifInterface object's existing exif
* tags.
*
* @param tags a collection of exif tags to set.
*/
fun setExif(tags : Collection<ExifTag>) : ExifInterface {
clearExif()
setTags(tags)
return this
}
/**
* 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 [.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.
*/
private fun setTag(tag : ExifTag) : ExifTag? =
mData.addTag(tag)
/**
* 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
*/
private fun setTags(tags : Collection<ExifTag>) : ExifInterface {
for(t in tags) setTag(t)
return this
}
@Throws(IOException::class)
fun writeExif(dstFilename : String) {
Log.i(TAG, "writeExif: $dstFilename")
// create a backup file
val dst_file = File(dstFilename)
val bak_file = 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.absolutePath, bak_file.absolutePath)
// Now switch bak into dst
// Log.d( TAG, "rename the bak into dst" );
bak_file.renameTo(dst_file)
} finally {
// deleting backup file
bak_file.delete()
}
}
@Throws(IOException::class)
fun writeExif(srcFilename : String, dstFilename : String) {
Log.i(TAG, "writeExif: $dstFilename")
// src and dst cannot be the same
if(srcFilename == 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(srcFilename).use { input ->
FileOutputStream(dstFilename).use { output ->
val position = writeExif_internal(input, output, mData)
// 7. write the rest of the image..
val in_channel = input.channel
val out_channel = output.channel
in_channel.transferTo(position.toLong(), in_channel.size() - position, out_channel)
output.flush()
}
}
}
@Throws(IOException::class)
fun writeExif(input : InputStream, dstFilename : String) {
Log.i(TAG, "writeExif: $dstFilename")
// inpur is used *ONLY* to read the image uncompressed data
// exif tags are not used here
val output = FileOutputStream(dstFilename)
writeExif_internal(input, output, mData)
// 7. write the rest of the image..
IOUtils.copy(input, output)
output.flush()
output.close()
}
// input is used *ONLY* to read the image uncompressed data
// exif tags are not used here
@Throws(IOException::class)
fun writeExif(input : Bitmap, dstFilename : String, quality : Int) {
Log.i(TAG, "writeExif: $dstFilename")
ByteArrayOutputStream().use { out ->
input.compress(Bitmap.CompressFormat.JPEG, quality, out)
ByteArrayInputStream(out.toByteArray()).use { inStream ->
writeExif(inStream, dstFilename)
}
}
}
/**
* Returns a list of ExifTags that share a TID (which can be obtained by
* calling [.getTrueTagKey] on a defined tag constant) or null if none
* exist.
*
* @param tagId a TID as defined in the exif standard (or with
* [.defineTag]).
* @return a List of [ExifTag]s.
*/
fun getTagsForTagId(tagId : Short) : List<ExifTag>? {
return mData.getAllTagsForTagId(tagId)
}
/**
* Returns a list of ExifTags that share an IFD (which can be obtained by
* calling [.getTrueIfd] on a defined tag constant) or null if none
* exist.
*
* @param ifdId an IFD as defined in the exif standard (or with
* [.defineTag]).
* @return a List of [ExifTag]s.
*/
fun getTagsForIfdId(ifdId : Int) : List<ExifTag>? {
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. [.TAG_IMAGE_WIDTH].
* @return an [ExifTag] or null if none exists.
*/
fun getTag(tagId : Int) : ExifTag? {
val ifdId = getDefinedTagDefaultIfd(tagId)
return getTag(tagId, ifdId)
}
/**
* Gets the default IFD for a tag.
*
* @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
* @return the default IFD for a tag definition or [.IFD_NULL] if no
* definition exists.
*/
private fun getDefinedTagDefaultIfd(tagId : Int) : Int {
val info = tagInfo.get(tagId)
return if(info == DEFINITION_NULL) {
IFD_NULL
} else getTrueIfd(tagId)
}
/**
* Gets an ExifTag for an IFD other than the tag's default.
*
* @see .getTag
*/
private fun getTag(tagId : Int, ifdId : Int) : ExifTag? {
return if(! ExifTag.isValidIfd(ifdId)) {
null
} else mData.getTag(getTrueTagKey(tagId), ifdId)
}
/**
* 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. [.TAG_IMAGE_WIDTH].
* @return the value of the ExifTag or null if none exists.
*/
fun getTagValue(tagId : Int) : Any? {
val ifdId = getDefinedTagDefaultIfd(tagId)
return getTagValue(tagId, ifdId)
}
/**
* Gets a tag value for an IFD other than the tag's default.
*
* @see .getTagValue
*/
private fun getTagValue(tagId : Int, ifdId : Int) : Any? {
val t = getTag(tagId, ifdId)
return t?.getValue()
}
private fun getTagStringValue(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : String? =
getTag(tagId, ifdId)?.valueAsString
// array
private fun getTagLongValues(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : LongArray? =
getTag(tagId, ifdId)?.valueAsLongs
private fun getTagIntValues(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : IntArray? =
getTag(tagId, ifdId)?.valueAsInts
private fun getTagByteValues(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : ByteArray? =
getTag(tagId, ifdId)?.valueAsBytes
private fun getTagRationalValues(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : Array<Rational>? =
getTag(tagId, ifdId)?.valueAsRationals
// single value
fun getTagLongValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Long? =
getTagLongValues(tagId, ifdId)?.firstOrNull()
fun getTagIntValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Int? =
getTagIntValues(tagId, ifdId)?.firstOrNull()
private fun getTagByteValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Byte? =
getTagByteValues(tagId, ifdId)?.firstOrNull()
private fun getTagRationalValue(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : Rational? =
getTagRationalValues(tagId, ifdId)?.firstOrNull()
/**
* Checks whether a tag has a defined number of elements.
*
* @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
* @return true if the tag has a defined number of elements.
*/
fun isTagCountDefined(tagId : Int) : Boolean {
val info = tagInfo.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
}
/**
* Gets the defined number of elements for a tag.
*
* @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
* @return the number of elements or [ExifTag.SIZE_UNDEFINED] if the
* tag or the number of elements is not defined.
*/
fun getDefinedTagCount(tagId : Int) : Int =
when(val info = tagInfo.get(tagId)) {
0 -> ExifTag.SIZE_UNDEFINED
else -> getComponentCountFromInfo(info)
}
/**
* Gets the number of elements for an ExifTag in a given IFD.
*
* @param tagId a defined tag constant, e.g. [.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.
*/
fun getActualTagCount(tagId : Int, ifdId : Int) : Int {
val t = getTag(tagId, ifdId) ?: return 0
return t.componentCount
}
// Gets the defined type for a tag.
// tagId : a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
fun getDefinedTagType(tagId : Int) : Short {
val info = tagInfo.get(tagId)
return if(info == 0) - 1 else getTypeFromInfo(info)
}
fun buildUninitializedTag(tagId : Int) : ExifTag? {
val info = tagInfo.get(tagId)
if(info == 0) {
return null
}
val type = getTypeFromInfo(info)
val definedCount = getComponentCountFromInfo(info)
val hasDefinedCount = definedCount != ExifTag.SIZE_UNDEFINED
val ifdId = getTrueIfd(tagId)
return ExifTag(getTrueTagKey(tagId), type, definedCount, ifdId, hasDefinedCount)
}
/**
* 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. [.TAG_IMAGE_WIDTH].
* @param ifdId the IFD that the ExifTag is in.
* @param tagValue 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
*/
private fun setTagValue(
tagId : Int,
tagValue : Any,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : Boolean =
getTag(tagId, ifdId)?.setValueAny(tagValue) ?: false
/**
* Removes the ExifTag for a tag constant from the given IFD.
*
* @param tagId a tag constant, e.g. [.TAG_IMAGE_WIDTH].
* @param ifdId the IFD of the ExifTag to remove.
*/
private fun deleteTag(
tagId : Int,
ifdId : Int = getDefinedTagDefaultIfd(tagId)
) : ExifInterface {
mData.removeTag(getTrueTagKey(tagId), ifdId)
return this
}
/**
* 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
* @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. [.TAG_IMAGE_WIDTH]) or
* [.TAG_NULL] if the definition could not be made.
*/
fun setTagDefinition(
tagId : Short,
defaultIfd : Int,
tagType : Short,
defaultComponentCount : Short,
allowedIfds : IntArray
) : Int {
if(sBannedDefines.contains(tagId)) {
return TAG_NULL
}
if(ExifTag.isValidType(tagType) && ExifTag.isValidIfd(defaultIfd)) {
val tagDef = defineTag(defaultIfd, tagId)
if(tagDef == TAG_NULL) {
return TAG_NULL
}
val otherDefs = getTagDefinitionsForTagId(tagId)
val infos = tagInfo
// Make sure defaultIfd is in allowedIfds
var defaultCheck = false
for(i in allowedIfds) {
if(defaultIfd == i) {
defaultCheck = true
}
if(! ExifTag.isValidIfd(i)) {
return TAG_NULL
}
}
if(! defaultCheck) {
return TAG_NULL
}
val ifdFlags = getFlagsFromAllowedIfds(allowedIfds)
// Make sure no identical tags can exist in allowedIfds
if(otherDefs != null) {
for(def in otherDefs) {
val tagInfo = infos.get(def)
val allowedFlags = getAllowedIfdFlagsFromInfo(tagInfo)
if(ifdFlags and allowedFlags != 0) {
return TAG_NULL
}
}
}
tagInfo.put(
tagDef,
ifdFlags shl 24 or (tagType shl 16) or defaultComponentCount.toInt()
)
return tagDef
}
return TAG_NULL
}
fun getTagDefinition(tagId : Short, defaultIfd : Int) : Int =
tagInfo.get(defineTag(defaultIfd, tagId))
private fun getTagDefinitionsForTagId(tagId : Short) : IntArray? {
val ifds = IfdData.list
val defs = IntArray(ifds.size)
var counter = 0
for(i in ifds) {
val def = defineTag(i, tagId)
if(tagInfo.get(def) != DEFINITION_NULL) {
defs[counter ++] = def
}
}
return if(counter == 0) null else defs.copyOfRange(0, counter)
}
fun getTagDefinitionForTag(tag : ExifTag) : Int =
getTagDefinitionForTag(tag.tagId, tag.dataType, tag.componentCount, tag.ifd)
private fun getTagDefinitionForTag(
tagId : Short,
type : Short,
count : Int,
ifd : Int
) : Int {
getTagDefinitionsForTagId(tagId)?.forEach { i ->
val info = tagInfo.get(i)
val def_type = getTypeFromInfo(info)
val def_count = getComponentCountFromInfo(info)
val def_ifds = getAllowedIfdsFromInfo(info)
var valid_ifd = false
if(def_ifds != null) {
for(j in def_ifds) {
if(j == ifd) {
valid_ifd = true
break
}
}
}
if(valid_ifd
&& type == def_type
&& (count == def_count || def_count == ExifTag.SIZE_UNDEFINED)
) {
return i
}
}
return TAG_NULL
}
/**
* Removes a tag definition for given defined tag constant.
*
* @param tagId a defined tag constant, e.g. [.TAG_IMAGE_WIDTH].
*/
fun removeTagDefinition(tagId : Int) {
tagInfo.delete(tagId)
}
// /**
// * Resets tag definitions to the default ones.
// */
// fun resetTagDefinitions() {
// mTagInfo = null
// }
/**
* 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.
*/
fun setCompressedThumbnail(thumb : Bitmap) : Boolean {
val thumbnail = ByteArrayOutputStream()
return if(! thumb.compress(Bitmap.CompressFormat.JPEG, 90, thumbnail)) {
false
} else 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.
*/
private fun setCompressedThumbnail(thumb : ByteArray) : Boolean {
mData.clearThumbnailAndStrips()
mData.compressedThumbnail = thumb
return true
}
/**
* Clears the compressed thumbnail if it exists.
*/
fun removeCompressedThumbnail() {
mData.compressedThumbnail = null
}
/**
* 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.
*/
fun getAltitude(defaultValue : Double) : Double {
val ref = getTagByteValue(TAG_GPS_ALTITUDE_REF)
val gpsAltitude = getTagRationalValue(TAG_GPS_ALTITUDE)
var seaLevel = 1
if(null != ref) {
seaLevel = if(ref == 1.toByte()) - 1 else 1
}
return if(gpsAltitude != null) {
gpsAltitude.toDouble() * seaLevel
} else defaultValue
}
/**
* Creates, formats, and sets the DateTimeStamp tag for one of:
* [.TAG_DATE_TIME], [.TAG_DATE_TIME_DIGITIZED],
* [.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.
*/
fun addDateTimeStampTag(tagId : Int, timestamp : Long, timezone : TimeZone) : Boolean {
if(tagId == TAG_DATE_TIME || tagId == TAG_DATE_TIME_DIGITIZED || tagId == TAG_DATE_TIME_ORIGINAL) {
mDateTimeStampFormat.timeZone = timezone
val t = buildTag(tagId, mDateTimeStampFormat.format(timestamp)) ?: return false
setTag(t)
} else {
return false
}
return true
}
/**
* 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
* [ExifTag.setValue] for this tag's datatype would fail.
*
* @param tagId a tag constant, e.g. [.TAG_IMAGE_WIDTH].
* @param ifdId the IFD that the tag should be in.
* @param tagValue the value of the tag to set.
* @return an ExifTag object or null if one could not be constructed.
* @see .buildTag
*/
fun buildTag(tagId : Int, tagValue : Any, ifdId : Int = getTrueIfd(tagId)) : ExifTag? {
val info = tagInfo.get(tagId)
if(info == 0 || ! isIfdAllowed(info, ifdId)) return null
val definedCount = getComponentCountFromInfo(info)
val t = ExifTag(
tagId = getTrueTagKey(tagId),
dataType = getTypeFromInfo(info),
componentCount = definedCount,
ifd = ifdId,
mHasDefinedDefaultComponentCount = definedCount != ExifTag.SIZE_UNDEFINED
)
return when {
t.setValueAny(tagValue) -> t
else -> null
}
}
/**
* 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.
*/
fun addGpsTags(latitude : Double, longitude : Double) : Boolean {
val latTag = buildTag(TAG_GPS_LATITUDE, toExifLatLong(latitude))
val longTag = buildTag(TAG_GPS_LONGITUDE, toExifLatLong(longitude))
val latRefTag = buildTag(
TAG_GPS_LATITUDE_REF,
if(latitude >= 0) GpsLatitudeRef.NORTH else GpsLatitudeRef.SOUTH
)
val longRefTag = buildTag(
TAG_GPS_LONGITUDE_REF,
if(longitude >= 0) GpsLongitudeRef.EAST else GpsLongitudeRef.WEST
)
if(latTag == null || longTag == null || latRefTag == null || longRefTag == null) {
return false
}
setTag(latTag)
setTag(longTag)
setTag(latRefTag)
setTag(longRefTag)
return true
}
/**
* Creates and sets the GPS timestamp tag.
*
* @param timestamp a GPS timestamp.
* @return true if success, false if could not be created or set.
*/
fun addGpsDateTimeStampTag(timestamp : Long) : Boolean {
var t = buildTag(TAG_GPS_DATE_STAMP, mGPSDateStampFormat.format(timestamp)) ?: return false
setTag(t)
mGPSTimeStampCalendar.timeInMillis = timestamp
t = buildTag(
TAG_GPS_TIME_STAMP,
arrayOf(
Rational(mGPSTimeStampCalendar.get(Calendar.HOUR_OF_DAY).toLong(), 1),
Rational(mGPSTimeStampCalendar.get(Calendar.MINUTE).toLong(), 1),
Rational(mGPSTimeStampCalendar.get(Calendar.SECOND).toLong(), 1)
)
) ?: return false
setTag(t)
return true
}
/**
* Constants for [.TAG_ORIENTATION]. They can be interpreted as
* follows:
*
* * TOP_LEFT is the normal orientation.
* * TOP_RIGHT is a left-right mirror.
* * BOTTOM_LEFT is a 180 degree rotation.
* * BOTTOM_RIGHT is a top-bottom mirror.
* * LEFT_TOP is mirrored about the top-left<->bottom-right axis.
* * RIGHT_TOP is a 90 degree clockwise rotation.
* * LEFT_BOTTOM is mirrored about the top-right<->bottom-left axis.
* * RIGHT_BOTTOM is a 270 degree clockwise rotation.
*
*/
object Orientation {
const val TOP_LEFT : Short = 1
const val TOP_RIGHT : Short = 2
const val BOTTOM_RIGHT : Short = 3
const val BOTTOM_LEFT : Short = 4
const val LEFT_TOP : Short = 5
const val RIGHT_TOP : Short = 6
const val RIGHT_BOTTOM : Short = 7
const val LEFT_BOTTOM : Short = 8
}
/**
* Constants for [.TAG_Y_CB_CR_POSITIONING]
*/
object YCbCrPositioning {
const val CENTERED : Short = 1
const val CO_SITED : Short = 2
}
/**
* Constants for [.TAG_COMPRESSION]
*/
object Compression {
const val UNCOMPRESSION : Short = 1
const val JPEG : Short = 6
}
/**
* Constants for [.TAG_RESOLUTION_UNIT]
*/
object ResolutionUnit {
const val INCHES : Short = 2
const val CENTIMETERS : Short = 3
const val MILLIMETERS : Short = 4
const val MICROMETERS : Short = 5
}
/**
* Constants for [.TAG_PHOTOMETRIC_INTERPRETATION]
*/
object PhotometricInterpretation {
const val RGB : Short = 2
const val YCBCR : Short = 6
}
/**
* Constants for [.TAG_PLANAR_CONFIGURATION]
*/
object PlanarConfiguration {
const val CHUNKY : Short = 1
const val PLANAR : Short = 2
}
// Convenience methods:
/**
* Constants for [.TAG_EXPOSURE_PROGRAM]
*/
object ExposureProgram {
const val NOT_DEFINED : Short = 0
const val MANUAL : Short = 1
const val NORMAL_PROGRAM : Short = 2
const val APERTURE_PRIORITY : Short = 3
const val SHUTTER_PRIORITY : Short = 4
const val CREATIVE_PROGRAM : Short = 5
const val ACTION_PROGRAM : Short = 6
const val PROTRAIT_MODE : Short = 7
const val LANDSCAPE_MODE : Short = 8
}
/**
* Constants for [.TAG_METERING_MODE]
*/
object MeteringMode {
const val UNKNOWN : Short = 0
const val AVERAGE : Short = 1
const val CENTER_WEIGHTED_AVERAGE : Short = 2
const val SPOT : Short = 3
const val MULTISPOT : Short = 4
const val PATTERN : Short = 5
const val PARTAIL : Short = 6
const val OTHER : Short = 255
}
/**
* Constants for [.TAG_FLASH] As the definition in Jeita EXIF 2.2
*/
object Flash {
/**
* first bit
*/
enum class FlashFired {
NO, YES
}
/**
* Values for bits 1 and 2 indicating the status of returned light
*/
enum class StrobeLightDetection {
NO_DETECTION, RESERVED, LIGHT_NOT_DETECTED, LIGHT_DETECTED
}
/**
* Values for bits 3 and 4 indicating the camera's flash mode
*/
enum class CompulsoryMode {
UNKNOWN,
FIRING,
SUPPRESSION,
AUTO
}
/**
* Values for bit 5 indicating the presence of a flash function.
*/
enum class FlashFunction {
FUNCTION_PRESENT,
FUNCTION_NOR_PRESENT
}
/**
* Values for bit 6 indicating the camera's red-eye mode.
*/
enum class RedEyeMode {
NONE,
SUPPORTED
}
}
/**
* Constants for [.TAG_COLOR_SPACE]
*/
object ColorSpace {
const val SRGB : Short = 1
const val UNCALIBRATED = 0xFFFF.toShort()
}
/**
* Constants for [.TAG_EXPOSURE_MODE]
*/
object ExposureMode {
const val AUTO_EXPOSURE : Short = 0
const val MANUAL_EXPOSURE : Short = 1
const val AUTO_BRACKET : Short = 2
}
/**
* Constants for [.TAG_WHITE_BALANCE]
*/
object WhiteBalance {
const val AUTO : Short = 0
const val MANUAL : Short = 1
}
/**
* Constants for [.TAG_SCENE_CAPTURE_TYPE]
*/
object SceneCapture {
const val STANDARD : Short = 0
const val LANDSCAPE : Short = 1
const val PROTRAIT : Short = 2
const val NIGHT_SCENE : Short = 3
}
/**
* Constants for [.TAG_COMPONENTS_CONFIGURATION]
*/
object ComponentsConfiguration {
const val NOT_EXIST : Short = 0
const val Y : Short = 1
const val CB : Short = 2
const val CR : Short = 3
const val R : Short = 4
const val G : Short = 5
const val B : Short = 6
}
/**
* Constants for [.TAG_LIGHT_SOURCE]
*/
object LightSource {
const val UNKNOWN : Short = 0
const val DAYLIGHT : Short = 1
const val FLUORESCENT : Short = 2
const val TUNGSTEN : Short = 3
const val FLASH : Short = 4
const val FINE_WEATHER : Short = 9
const val CLOUDY_WEATHER : Short = 10
const val SHADE : Short = 11
const val DAYLIGHT_FLUORESCENT : Short = 12
const val DAY_WHITE_FLUORESCENT : Short = 13
const val COOL_WHITE_FLUORESCENT : Short = 14
const val WHITE_FLUORESCENT : Short = 15
const val STANDARD_LIGHT_A : Short = 17
const val STANDARD_LIGHT_B : Short = 18
const val STANDARD_LIGHT_C : Short = 19
const val D55 : Short = 20
const val D65 : Short = 21
const val D75 : Short = 22
const val D50 : Short = 23
const val ISO_STUDIO_TUNGSTEN : Short = 24
const val OTHER : Short = 255
}
/**
* Constants for [.TAG_SENSING_METHOD]
*/
object SensingMethod {
const val NOT_DEFINED : Short = 1
const val ONE_CHIP_COLOR : Short = 2
const val TWO_CHIP_COLOR : Short = 3
const val THREE_CHIP_COLOR : Short = 4
const val COLOR_SEQUENTIAL_AREA : Short = 5
const val TRILINEAR : Short = 7
const val COLOR_SEQUENTIAL_LINEAR : Short = 8
}
/**
* Constants for [.TAG_FILE_SOURCE]
*/
object FileSource {
const val DSC : Short = 3
}
/**
* Constants for [.TAG_SCENE_TYPE]
*/
object SceneType {
const val DIRECT_PHOTOGRAPHED : Short = 1
}
/**
* Constants for [.TAG_GAIN_CONTROL]
*/
object GainControl {
const val NONE : Short = 0
const val LOW_UP : Short = 1
const val HIGH_UP : Short = 2
const val LOW_DOWN : Short = 3
const val HIGH_DOWN : Short = 4
}
/**
* Constants for [.TAG_CONTRAST]
*/
object Contrast {
const val NORMAL : Short = 0
const val SOFT : Short = 1
const val HARD : Short = 2
}
/**
* Constants for [.TAG_SATURATION]
*/
object Saturation {
const val NORMAL : Short = 0
const val LOW : Short = 1
const val HIGH : Short = 2
}
/**
* Constants for [.TAG_SHARPNESS]
*/
object Sharpness {
const val NORMAL : Short = 0
const val SOFT : Short = 1
const val HARD : Short = 2
}
/**
* Constants for [.TAG_SUBJECT_DISTANCE]
*/
object SubjectDistance {
const val UNKNOWN : Short = 0
const val MACRO : Short = 1
const val CLOSE_VIEW : Short = 2
const val DISTANT_VIEW : Short = 3
}
/**
* Constants for [.TAG_GPS_LATITUDE_REF],
* [.TAG_GPS_DEST_LATITUDE_REF]
*/
object GpsLatitudeRef {
const val NORTH = "N"
const val SOUTH = "S"
}
/**
* Constants for [.TAG_GPS_LONGITUDE_REF],
* [.TAG_GPS_DEST_LONGITUDE_REF]
*/
object GpsLongitudeRef {
const val EAST = "E"
const val WEST = "W"
}
/**
* Constants for [.TAG_GPS_ALTITUDE_REF]
*/
object GpsAltitudeRef {
const val SEA_LEVEL : Short = 0
const val SEA_LEVEL_NEGATIVE : Short = 1
}
/**
* Constants for [.TAG_GPS_STATUS]
*/
object GpsStatus {
const val IN_PROGRESS = "A"
const val INTEROPERABILITY = "V"
}
/**
* Constants for [.TAG_GPS_MEASURE_MODE]
*/
object GpsMeasureMode {
const val MODE_2_DIMENSIONAL = "2"
const val MODE_3_DIMENSIONAL = "3"
}
/**
* Constants for [.TAG_GPS_SPEED_REF],
* [.TAG_GPS_DEST_DISTANCE_REF]
*/
object GpsSpeedRef {
const val KILOMETERS = "K"
const val MILES = "M"
const val KNOTS = "N"
}
/**
* Constants for [.TAG_GPS_TRACK_REF],
* [.TAG_GPS_IMG_DIRECTION_REF], [.TAG_GPS_DEST_BEARING_REF]
*/
object GpsTrackRef {
const val TRUE_DIRECTION = "T"
const val MAGNETIC_DIRECTION = "M"
}
/**
* Constants for [.TAG_GPS_DIFFERENTIAL]
*/
object GpsDifferential {
const val WITHOUT_DIFFERENTIAL_CORRECTION : Short = 0
const val DIFFERENTIAL_CORRECTION_APPLIED : Short = 1
}
/**
* Constants for the jpeg process algorithm used.
*
* @see .getJpegProcess
*/
object JpegProcess {
const val BASELINE = 0xFFC0.toShort()
const val EXTENDED_SEQUENTIAL = 0xFFC1.toShort()
const val PROGRESSIVE = 0xFFC2.toShort()
const val LOSSLESS = 0xFFC3.toShort()
const val DIFFERENTIAL_SEQUENTIAL = 0xFFC5.toShort()
const val DIFFERENTIAL_PROGRESSIVE = 0xFFC6.toShort()
const val DIFFERENTIAL_LOSSLESS = 0xFFC7.toShort()
const val EXTENDED_SEQ_ARITHMETIC_CODING = 0xFFC9.toShort()
const val PROGRESSIVE_AIRTHMETIC_CODING = 0xFFCA.toShort()
const val LOSSLESS_AITHMETIC_CODING = 0xFFCB.toShort()
const val DIFFERENTIAL_SEQ_ARITHMETIC_CODING = 0xFFCD.toShort()
const val DIFFERENTIAL_PROGRESSIVE_ARITHMETIC_CODING = 0xFFCE.toShort()
const val DIFFERENTIAL_LOSSLESS_ARITHMETIC_CODING = 0xFFCF.toShort()
}
/**
* Constants for the [.TAG_SENSITIVITY_TYPE] tag
*/
object SensitivityType {
const val UNKNOWN : Short = 0
/**
* Standard output sensitivity
*/
const val SOS : Short = 1
/**
* Recommended exposure index
*/
const val REI : Short = 2
/**
* ISO Speed
*/
const val ISO : Short = 3
/**
* Standard output sensitivity and Recommended output index
*/
const val SOS_REI : Short = 4
/**
* Standard output sensitivity and ISO speed
*/
const val SOS_ISO : Short = 5
/**
* Recommended output index and ISO Speed
*/
const val REI_ISO : Short = 6
/**
* Standard output sensitivity and Recommended output index and ISO Speed
*/
const val SOS_REI_ISO : Short = 7
}
/**
* Options for calling [.readExif], [.readExif],
* [.readExif]
*/
object Options {
/**
* Option bit to request to parse IFD0.
*/
const val OPTION_IFD_0 = 1
/**
* Option bit to request to parse IFD1.
*/
const val OPTION_IFD_1 = 1 shl 1
/**
* Option bit to request to parse Exif-IFD.
*/
const val OPTION_IFD_EXIF = 1 shl 2
/**
* Option bit to request to parse GPS-IFD.
*/
const val OPTION_IFD_GPS = 1 shl 3
/**
* Option bit to request to parse Interoperability-IFD.
*/
const val OPTION_IFD_INTEROPERABILITY = 1 shl 4
/**
* Option bit to request to parse thumbnail.
*/
const val OPTION_THUMBNAIL = 1 shl 5
/**
* Option bit to request all the options
*/
const val OPTION_ALL =
OPTION_IFD_0 xor OPTION_IFD_1 xor OPTION_IFD_EXIF xor OPTION_IFD_GPS xor OPTION_IFD_INTEROPERABILITY xor OPTION_THUMBNAIL
}
@Suppress("unused")
companion object {
private const val TAG = "ExifInterface"
const val TAG_NULL = - 1
const val IFD_NULL = - 1
const val DEFINITION_NULL = 0
/**
* Tag constants for Jeita EXIF 2.2
*/
// IFD 0
private val TAG_IMAGE_WIDTH = defineTag(IfdData.TYPE_IFD_0, 0x0100.toShort())
private val TAG_IMAGE_LENGTH =
defineTag(IfdData.TYPE_IFD_0, 0x0101.toShort()) // Image height
private val TAG_BITS_PER_SAMPLE = defineTag(IfdData.TYPE_IFD_0, 0x0102.toShort())
/**
* Value is unsigned int.<br></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.
*
* * 1 = uncompressed
* * 6 = JPEG compression (thumbnails only)
* * Other = reserved
*/
private val TAG_COMPRESSION = defineTag(IfdData.TYPE_IFD_0, 0x0103.toShort())
private val TAG_PHOTOMETRIC_INTERPRETATION = defineTag(IfdData.TYPE_IFD_0, 0x0106.toShort())
private val TAG_IMAGE_DESCRIPTION = defineTag(IfdData.TYPE_IFD_0, 0x010E.toShort())
/**
* Value is ascii string<br></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.
*/
private val TAG_MAKE = defineTag(IfdData.TYPE_IFD_0, 0x010F.toShort())
/**
* Value is ascii string<br></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.
*/
private val TAG_MODEL = defineTag(IfdData.TYPE_IFD_0, 0x0110.toShort())
val TAG_STRIP_OFFSETS = defineTag(IfdData.TYPE_IFD_0, 0x0111.toShort())
/**
* Value is int<br></br>
* The orientation of the camera relative to the scene, when the image was captured. The start point of stored data is:
*
* * '0' undefined
* * '1' normal
* * '2' flip horizontal
* * '3' rotate 180
* * '4' flip vertical
* * '5' transpose, flipped about top-left <--> bottom-right axis
* * '6' rotate 90 cw
* * '7' transverse, flipped about top-right <--> bottom-left axis
* * '8' rotate 270
* * '9' undefined
*
*/
val TAG_ORIENTATION = defineTag(IfdData.TYPE_IFD_0, 0x0112.toShort())
private val TAG_SAMPLES_PER_PIXEL = defineTag(IfdData.TYPE_IFD_0, 0x0115.toShort())
private val TAG_ROWS_PER_STRIP = defineTag(IfdData.TYPE_IFD_0, 0x0116.toShort())
val TAG_STRIP_BYTE_COUNTS = defineTag(IfdData.TYPE_IFD_0, 0x0117.toShort())
private val TAG_INTEROP_VERSION =
defineTag(IfdData.TYPE_IFD_INTEROPERABILITY, 0x0002.toShort())
/**
* Value is unsigned double.<br></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.
*/
private val TAG_X_RESOLUTION = defineTag(IfdData.TYPE_IFD_0, 0x011A.toShort())
/**
* @see .TAG_X_RESOLUTION
*/
private val TAG_Y_RESOLUTION = defineTag(IfdData.TYPE_IFD_0, 0x011B.toShort())
private val TAG_PLANAR_CONFIGURATION = defineTag(IfdData.TYPE_IFD_0, 0x011C.toShort())
/**
* Value is unsigned int.<br></br>
* Unit of XResolution(0x011a)/YResolution(0x011b)
*
* * '1' means no-unit ( use inch )
* * '2' inch
* * '3' centimeter
* * '4' millimeter
* * '5' micrometer
*
*/
private val TAG_RESOLUTION_UNIT = defineTag(IfdData.TYPE_IFD_0, 0x0128.toShort())
private val TAG_TRANSFER_FUNCTION = defineTag(IfdData.TYPE_IFD_0, 0x012D.toShort())
/**
* Value is ascii string<br></br>
* Shows firmware(internal software of digicam) version number.
*/
private val TAG_SOFTWARE = defineTag(IfdData.TYPE_IFD_0, 0x0131.toShort())
/**
* Value is ascii string (20)<br></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)
*/
val TAG_DATE_TIME = defineTag(IfdData.TYPE_IFD_0, 0x0132.toShort())
/**
* Vallue is ascii String<br></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.
*/
private val TAG_ARTIST = defineTag(IfdData.TYPE_IFD_0, 0x013B.toShort())
private val TAG_WHITE_POINT = defineTag(IfdData.TYPE_IFD_0, 0x013E.toShort())
private val TAG_PRIMARY_CHROMATICITIES = defineTag(IfdData.TYPE_IFD_0, 0x013F.toShort())
private val TAG_Y_CB_CR_COEFFICIENTS = defineTag(IfdData.TYPE_IFD_0, 0x0211.toShort())
private val TAG_Y_CB_CR_SUB_SAMPLING = defineTag(IfdData.TYPE_IFD_0, 0x0212.toShort())
private val TAG_Y_CB_CR_POSITIONING = defineTag(IfdData.TYPE_IFD_0, 0x0213.toShort())
private val TAG_REFERENCE_BLACK_WHITE = defineTag(IfdData.TYPE_IFD_0, 0x0214.toShort())
/**
* Values is ascii string<br></br>
* Shows copyright information
*/
private val TAG_COPYRIGHT = defineTag(IfdData.TYPE_IFD_0, 0x8298.toShort())
val TAG_EXIF_IFD = defineTag(IfdData.TYPE_IFD_0, 0x8769.toShort())
val TAG_GPS_IFD = defineTag(IfdData.TYPE_IFD_0, 0x8825.toShort())
// IFD 1
val TAG_JPEG_INTERCHANGE_FORMAT = defineTag(IfdData.TYPE_IFD_1, 0x0201.toShort())
val TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = defineTag(IfdData.TYPE_IFD_1, 0x0202.toShort())
// IFD Exif Tags
/**
* Value is unsigned double<br></br>
* Exposure time (reciprocal of shutter speed). Unit is second
*/
private val TAG_EXPOSURE_TIME = defineTag(IfdData.TYPE_IFD_EXIF, 0x829A.toShort())
/**
* Value is unsigned double<br></br>
* The actual F-number(F-stop) of lens when the image was taken
*
* @see .TAG_APERTURE_VALUE
*/
val TAG_F_NUMBER = defineTag(IfdData.TYPE_IFD_EXIF, 0x829D.toShort())
/**
* Value is unsigned int.<br></br>
* Exposure program that the camera used when image was taken.
*
* * '1' means manual control
* * '2' program normal
* * '3' aperture priority
* * '4' shutter priority
* * '5' program creative (slow program)
* * '6' program action(high-speed program)
* * '7' portrait mode
* * '8' landscape mode.
*
*/
private val TAG_EXPOSURE_PROGRAM = defineTag(IfdData.TYPE_IFD_EXIF, 0x8822.toShort())
private val TAG_SPECTRAL_SENSITIVITY = defineTag(IfdData.TYPE_IFD_EXIF, 0x8824.toShort())
/**
* Value is unsigned int.<br></br>
* CCD sensitivity equivalent to Ag-Hr film speedrate.<br></br>
* Indicates the ISO Speed and ISO Latitude of the camera or input device as specified in ISO 12232
*/
private val TAG_ISO_SPEED_RATINGS = defineTag(IfdData.TYPE_IFD_EXIF, 0x8827.toShort())
private val TAG_OECF = defineTag(IfdData.TYPE_IFD_EXIF, 0x8828.toShort())
/**
* ASCII string (4).<br></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
*/
private val TAG_EXIF_VERSION = defineTag(IfdData.TYPE_IFD_EXIF, 0x9000.toShort())
/**
* Value is ascii string (20)<br></br>
* Date/Time of original image taken. This value should not be modified by user program.
*/
val TAG_DATE_TIME_ORIGINAL = defineTag(IfdData.TYPE_IFD_EXIF, 0x9003.toShort())
/**
* Value is ascii string (20)<br></br>
* Date/Time of image digitized. Usually, it contains the same value of DateTimeOriginal(0x9003).
*/
val TAG_DATE_TIME_DIGITIZED = defineTag(IfdData.TYPE_IFD_EXIF, 0x9004.toShort())
private val TAG_COMPONENTS_CONFIGURATION =
defineTag(IfdData.TYPE_IFD_EXIF, 0x9101.toShort())
private val TAG_COMPRESSED_BITS_PER_PIXEL =
defineTag(IfdData.TYPE_IFD_EXIF, 0x9102.toShort())
/**
* Value is signed double.<br></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.
*/
private val TAG_SHUTTER_SPEED_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9201.toShort())
/**
* Value is unsigned double<br></br>
* The actual aperture value of lens when the image was taken.<br></br>
* To convert this value to ordinary F-number(F-stop), calculate this value's power of root 2 (=1.4142).<br></br>
* For example, if value is '5', F-number is 1.4142^5 = F5.6<br></br>
*
*
* <pre>
* FNumber = Math.exp( ApertureValue * Math.log( 2 ) * 0.5 );
</pre> *
*
* @see .TAG_F_NUMBER
*/
val TAG_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9202.toShort())
/**
* Value is signed double<br></br>
* Brightness of taken subject, unit is EV.
*/
private val TAG_BRIGHTNESS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9203.toShort())
/**
* Value is signed double.<br></br>
* The exposure bias. The unit is the APEX value. Ordinarily it is given in the range of -99.99 to 99.99
*/
private val TAG_EXPOSURE_BIAS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9204.toShort())
/**
* Value is unsigned double.<br></br>
* Maximum aperture value of lens.<br></br>
* You can convert to F-number by calculating power of root 2 (same process of ApertureValue(0x9202).<br></br>
*
*
* <pre>
* FNumber = Math.exp( MaxApertureValue * Math.log( 2 ) * 0.5 )
</pre> *
*/
private val TAG_MAX_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9205.toShort())
/**
* Value if signed double.<br></br>
* Distance to focus point, unit is meter. If value < 0 then focus point is infinite
*/
private val TAG_SUBJECT_DISTANCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9206.toShort())
/**
* Value is unsigned int.<br></br>
* Exposure metering method:
*
* * 0 = unknown
* * 1 = Average
* * 2 = CenterWeightedAverage
* * 3 = Spot
* * 4 = MultiSpot
* * 5 = Pattern
* * 6 = Partial
* * Other = reserved
* * 255 = other
*
*/
private val TAG_METERING_MODE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9207.toShort())
/**
* Value is unsigned int.<br></br>
* Light source, actually this means white balance setting.
*
* * 0 = means auto
* * 1 = Daylight
* * 2 = Fluorescent
* * 3 = Tungsten (incandescent light)
* * 4 = Flash
* * 9 = Fine weather
* * 10 = Cloudy weather
* * 11 = Shade
* * 12 = Daylight fluorescent (D 5700 - 7100K)
* * 13 = Day white fluorescent (N 4600 - 5400K)
* * 14 = Cool white fluorescent (W 3900 - 4500K)
* * 15 = White fluorescent (WW 3200 - 3700K)
* * 17 = Standard light A
* * 18 = Standard light B
* * 19 = Standard light C
* * 20 = D55
* * 21 = D65
* * 22 = D75
* * 23 = D50
* * 24 = ISO studio tungsten
* * 255 = other light source
* * Other = reserved
*
*/
private val TAG_LIGHT_SOURCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9208.toShort())
/**
* Value is unsigned integer<br></br>
* The 8 bits can be extracted and evaluated in this way:<br></br>
*
* 1. Bit 0 indicates the flash firing status
* 1. bits 1 and 2 indicate the flash return status
* 1. bits 3 and 4 indicate the flash mode
* 1. bit 5 indicates whether the flash function is present
* 1. and bit 6 indicates "red eye" mode
* 1. bit 7 unused
*
*
*
* Resulting Flash tag values are:<br></br>
*
* * 0000.H = Flash did not fire
* * 0001.H = Flash fired
* * 0005.H = Strobe return light not detected
* * 0007.H = Strobe return light detected
* * 0009.H = Flash fired, compulsory flash mode
* * 000D.H = Flash fired, compulsory flash mode, return light not detected
* * 000F.H = Flash fired, compulsory flash mode, return light detected
* * 0010.H = Flash did not fire, compulsory flash mode
* * 0018.H = Flash did not fire, auto mode
* * 0019.H = Flash fired, auto mode
* * 001D.H = Flash fired, auto mode, return light not detected
* * 001F.H = Flash fired, auto mode, return light detected
* * 0020.H = No flash function
* * 0041.H = Flash fired, red-eye reduction mode
* * 0045.H = Flash fired, red-eye reduction mode, return light not detected
* * 0047.H = Flash fired, red-eye reduction mode, return light detected
* * 0049.H = Flash fired, compulsory flash mode, red-eye reduction mode
* * 004D.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected
* * 004F.H = Flash fired, compulsory flash mode, red-eye reduction mode, return light detected
* * 0059.H = Flash fired, auto mode, red-eye reduction mode
* * 005D.H = Flash fired, auto mode, return light not detected, red-eye reduction mode
* * 005F.H = Flash fired, auto mode, return light detected, red-eye reduction mode
* * Other = reserved
*
*
* @see [http://www.exif.org/Exif2-2.PDF](http://www.exif.org/Exif2-2.PDF)
*/
private val TAG_FLASH = defineTag(IfdData.TYPE_IFD_EXIF, 0x9209.toShort())
/**
* Value is unsigned double<br></br>
* Focal length of lens used to take image. Unit is millimeter.
*/
private val TAG_FOCAL_LENGTH = defineTag(IfdData.TYPE_IFD_EXIF, 0x920A.toShort())
private val TAG_SUBJECT_AREA = defineTag(IfdData.TYPE_IFD_EXIF, 0x9214.toShort())
private val TAG_MAKER_NOTE = defineTag(IfdData.TYPE_IFD_EXIF, 0x927C.toShort())
val TAG_USER_COMMENT = defineTag(IfdData.TYPE_IFD_EXIF, 0x9286.toShort())
private val TAG_SUB_SEC_TIME = defineTag(IfdData.TYPE_IFD_EXIF, 0x9290.toShort())
private val TAG_SUB_SEC_TIME_ORIGINAL = defineTag(IfdData.TYPE_IFD_EXIF, 0x9291.toShort())
private val TAG_SUB_SEC_TIME_DIGITIZED = defineTag(IfdData.TYPE_IFD_EXIF, 0x9292.toShort())
private val TAG_FLASHPIX_VERSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA000.toShort())
/**
* Value is int.<br></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.
*
* * '1' = sRGB
* * 'FFFF' = Uncalibrated
* * 'other' = Reserved
*
*/
private val TAG_COLOR_SPACE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA001.toShort())
/**
* Value is unsigned int.<br></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.
*/
private val TAG_PIXEL_X_DIMENSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA002.toShort())
/**
* @see .TAG_PIXEL_X_DIMENSION
*/
private val TAG_PIXEL_Y_DIMENSION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA003.toShort())
private val TAG_RELATED_SOUND_FILE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA004.toShort())
val TAG_INTEROPERABILITY_IFD = defineTag(IfdData.TYPE_IFD_EXIF, 0xA005.toShort())
private val TAG_FLASH_ENERGY = defineTag(IfdData.TYPE_IFD_EXIF, 0xA20B.toShort())
private val TAG_SPATIAL_FREQUENCY_RESPONSE =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA20C.toShort())
/**
* Value is unsigned double.<br></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
*/
private val TAG_FOCAL_PLANE_X_RESOLUTION =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA20E.toShort())
/**
* @see .TAG_FOCAL_PLANE_X_RESOLUTION
*/
private val TAG_FOCAL_PLANE_Y_RESOLUTION =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA20F.toShort())
/**
* Value is unsigned int.<br></br>
* Unit of FocalPlaneXResoluton/FocalPlaneYResolution.
*
* * '1' means no-unit
* * '2' inch
* * '3' centimeter
* * '4' millimeter
* * '5' micrometer
*
*
*
* This tag can be used to calculate the CCD Width:
*
*
* <pre>
* CCDWidth = ( PixelXDimension * FocalPlaneResolutionUnit / FocalPlaneXResolution )
</pre> *
*/
private val TAG_FOCAL_PLANE_RESOLUTION_UNIT =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA210.toShort())
private val TAG_SUBJECT_LOCATION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA214.toShort())
private val TAG_EXPOSURE_INDEX = defineTag(IfdData.TYPE_IFD_EXIF, 0xA215.toShort())
/**
* Value is unsigned int.<br></br>
* Indicates the image sensor type on the camera or input device. The values are as follows:
*
* * 1 = Not defined
* * 2 = One-chip color area sensor
* * 3 = Two-chip color area sensor JEITA CP-3451 - 41
* * 4 = Three-chip color area sensor
* * 5 = Color sequential area sensor
* * 7 = Trilinear sensor
* * 8 = Color sequential linear sensor
* * Other = reserved
*
*/
private val TAG_SENSING_METHOD = defineTag(IfdData.TYPE_IFD_EXIF, 0xA217.toShort())
private val TAG_FILE_SOURCE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA300.toShort())
private val TAG_SCENE_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA301.toShort())
private val TAG_CFA_PATTERN = defineTag(IfdData.TYPE_IFD_EXIF, 0xA302.toShort())
private val TAG_CUSTOM_RENDERED = defineTag(IfdData.TYPE_IFD_EXIF, 0xA401.toShort())
/**
* Value is int.<br></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.
*
* * 0 = Auto exposure
* * 1 = Manual exposure
* * 2 = Auto bracket
* * Other = reserved
*
*/
private val TAG_EXPOSURE_MODE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA402.toShort())
private val TAG_WHITE_BALANCE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA403.toShort())
/**
* Value is double.<br></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
*/
private val TAG_DIGITAL_ZOOM_RATIO = defineTag(IfdData.TYPE_IFD_EXIF, 0xA404.toShort())
/**
* Value is unsigned int.<br></br>
* This tag indicates the equivalent focal length assuming a 35mm film camera, in mm.<br></br>
* Exif 2.2 tag, usually not present, it can be calculated by:
*
*
* <pre>
* CCDWidth = ( PixelXDimension * FocalplaneUnits / FocalplaneXRes );
* FocalLengthIn35mmFilm = ( FocalLength / CCDWidth * 36 + 0.5 );
</pre> *
*/
private val TAG_FOCAL_LENGTH_IN_35_MM_FILE =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA405.toShort())
/**
* Value is int.<br></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.
*
* * 0 = Standard
* * 1 = Landscape
* * 2 = Portrait
* * 3 = Night scene
* * Other = reserved
*
*/
private val TAG_SCENE_CAPTURE_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA406.toShort())
/**
* Value is int.<br></br>
* This tag indicates the degree of overall image gain adjustment.
*
* * 0 = None
* * 1 = Low gain up
* * 2 = High gain up
* * 3 = Low gain down
* * 4 = High gain down
* * Other = reserved
*
*/
private val TAG_GAIN_CONTROL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA407.toShort())
/**
* Value is int.<br></br>
* This tag indicates the direction of contrast processing applied by the camera when the image was shot.
*
* * 0 = Normal
* * 1 = Soft
* * 2 = Hard
* * Other = reserved
*
*/
private val TAG_CONTRAST = defineTag(IfdData.TYPE_IFD_EXIF, 0xA408.toShort())
/**
* Value is int.<br></br>
* This tag indicates the direction of saturation processing applied by the camera when the image was shot.
*
* * 0 = Normal
* * 1 = Low saturation
* * 2 = High saturation
* * Other = reserved
*
*/
private val TAG_SATURATION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA409.toShort())
/**
* Value is int.<br></br>
* This tag indicates the direction of sharpness processing applied by the camera when the image was shot
*
* * 0 = Normal
* * 1 = Soft
* * 2 = Hard
* * Other = reserved
*
*/
private val TAG_SHARPNESS = defineTag(IfdData.TYPE_IFD_EXIF, 0xA40A.toShort())
private val TAG_DEVICE_SETTING_DESCRIPTION =
defineTag(IfdData.TYPE_IFD_EXIF, 0xA40B.toShort())
/**
* Value is int.<br></br>
* This tag indicates the distance to the subject.
*
* * 0 = unknown
* * 1 = Macro
* * 2 = Close view
* * 3 = Distant view
* * Other = reserved
*
*/
private val TAG_SUBJECT_DISTANCE_RANGE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA40C.toShort())
/**
* [ExifTag.TYPE_ASCII]
*/
private val TAG_IMAGE_UNIQUE_ID = defineTag(IfdData.TYPE_IFD_EXIF, 0xA420.toShort())
/**
* Lens Specifications. The value it's a 4 rational containing:
*
* 1. Minimum focal length (in mm)
* 1. Maximum focal length (in mm)
* 1. Minimum F Number in the minimum focal length
* 1. Maximum F Number in the maximum focal length
*
*
*
* [ExifTag.TYPE_RATIONAL]
*
* @see it.sephiroth.android.library.exif2.ExifUtil.processLensSpecifications
* @since EXIF 2.3
*/
val TAG_LENS_SPECS = defineTag(IfdData.TYPE_IFD_EXIF, 0xA432.toShort())
/**
* Lens maker
* [ExifTag.TYPE_ASCII]
*
* @since EXIF 2.3
*/
private val TAG_LENS_MAKE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA433.toShort())
/**
* Lens model name and number
* [ExifTag.TYPE_ASCII]
*
* @since EXIF 2.3
*/
val TAG_LENS_MODEL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA434.toShort())
/**
* 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></br>
* Values:
*
* * 0: Unknown
* * 1: Standardoutputsensitivity(SOS)
* * 2: Recommended exposure index (REI)
* * 3: ISOspeed
* * 4: Standard output sensitivity (SOS) and recommended exposure index (REI)
* * 5: Standardoutputsensitivity(SOS)andISOspeed
* * 6: Recommendedexposureindex(REI)andISOspeed
* * 7: Standard output sensitivity (SOS) and recommended exposure index (REI) and ISO speed
* * Other: Reserved
*
*
*
* [ExifTag.TYPE_UNSIGNED_SHORT]
*
* @see it.sephiroth.android.library.exif2.ExifInterface.SensitivityType
*
* @since EXIF 2.3
*/
private val TAG_SENSITIVITY_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0x8830.toShort())
// IFD GPS tags
private val TAG_GPS_VERSION_ID = defineTag(IfdData.TYPE_IFD_GPS, 0.toShort())
/**
* Value is string(1)<br></br>
* Indicates whether the latitude is north or south latitude. The ASCII value 'N' indicates north latitude, and 'S' is south latitude.
*/
val TAG_GPS_LATITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 1.toShort())
/**
* Value is string.<br></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.
*/
val TAG_GPS_LATITUDE = defineTag(IfdData.TYPE_IFD_GPS, 2.toShort())
/**
* Value is string(1)<br></br>
* Indicates whether the longitude is east or west longitude. ASCII 'E' indicates east longitude, and 'W' is west longitude.
*/
val TAG_GPS_LONGITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 3.toShort())
/**
* Value is string.<br></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.
*/
val TAG_GPS_LONGITUDE = defineTag(IfdData.TYPE_IFD_GPS, 4.toShort())
/**
* Value is byte<br></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
*/
val TAG_GPS_ALTITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 5.toShort())
/**
* Value is string.<br></br>
* Indicates the altitude based on the reference in GPSAltitudeRef. Altitude is expressed as one RATIONAL value. The reference unit is meters.
*/
val TAG_GPS_ALTITUDE = defineTag(IfdData.TYPE_IFD_GPS, 6.toShort())
val TAG_GPS_TIME_STAMP = defineTag(IfdData.TYPE_IFD_GPS, 7.toShort())
private val TAG_GPS_SATTELLITES = defineTag(IfdData.TYPE_IFD_GPS, 8.toShort())
private val TAG_GPS_STATUS = defineTag(IfdData.TYPE_IFD_GPS, 9.toShort())
private val TAG_GPS_MEASURE_MODE = defineTag(IfdData.TYPE_IFD_GPS, 10.toShort())
private val TAG_GPS_DOP = defineTag(IfdData.TYPE_IFD_GPS, 11.toShort())
/**
* Value is string(1).<br></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.
*/
private val TAG_GPS_SPEED_REF = defineTag(IfdData.TYPE_IFD_GPS, 12.toShort())
/**
* Value is string.<br></br>
* Indicates the speed of GPS receiver movement
*/
private val TAG_GPS_SPEED = defineTag(IfdData.TYPE_IFD_GPS, 13.toShort())
private val TAG_GPS_TRACK_REF = defineTag(IfdData.TYPE_IFD_GPS, 14.toShort())
private val TAG_GPS_TRACK = defineTag(IfdData.TYPE_IFD_GPS, 15.toShort())
private val TAG_GPS_IMG_DIRECTION_REF = defineTag(IfdData.TYPE_IFD_GPS, 16.toShort())
private val TAG_GPS_IMG_DIRECTION = defineTag(IfdData.TYPE_IFD_GPS, 17.toShort())
private val TAG_GPS_MAP_DATUM = defineTag(IfdData.TYPE_IFD_GPS, 18.toShort())
private val TAG_GPS_DEST_LATITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 19.toShort())
private val TAG_GPS_DEST_LATITUDE = defineTag(IfdData.TYPE_IFD_GPS, 20.toShort())
val TAG_GPS_DEST_LONGITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 21.toShort())
val TAG_GPS_DEST_LONGITUDE = defineTag(IfdData.TYPE_IFD_GPS, 22.toShort())
private val TAG_GPS_DEST_BEARING_REF = defineTag(IfdData.TYPE_IFD_GPS, 23.toShort())
private val TAG_GPS_DEST_BEARING = defineTag(IfdData.TYPE_IFD_GPS, 24.toShort())
private val TAG_GPS_DEST_DISTANCE_REF = defineTag(IfdData.TYPE_IFD_GPS, 25.toShort())
private val TAG_GPS_DEST_DISTANCE = defineTag(IfdData.TYPE_IFD_GPS, 26.toShort())
private val TAG_GPS_PROCESSING_METHOD = defineTag(IfdData.TYPE_IFD_GPS, 27.toShort())
private val TAG_GPS_AREA_INFORMATION = defineTag(IfdData.TYPE_IFD_GPS, 28.toShort())
val TAG_GPS_DATE_STAMP = defineTag(IfdData.TYPE_IFD_GPS, 29.toShort())
private val TAG_GPS_DIFFERENTIAL = defineTag(IfdData.TYPE_IFD_GPS, 30.toShort())
// IFD Interoperability tags
private val TAG_INTEROPERABILITY_INDEX =
defineTag(IfdData.TYPE_IFD_INTEROPERABILITY, 1.toShort())
val DEFAULT_BYTE_ORDER : ByteOrder = ByteOrder.BIG_ENDIAN
private const val NULL_ARGUMENT_STRING = "Argument is null"
private const val GPS_DATE_FORMAT_STR = "yyyy:MM:dd"
private val mGPSDateStampFormat = SimpleDateFormat(GPS_DATE_FORMAT_STR, Locale.ENGLISH)
.apply { timeZone = TimeZone.getTimeZone("UTC") }
private const val DATETIME_FORMAT_STR = "yyyy:MM:dd kk:mm:ss"
private val mDateTimeStampFormat = SimpleDateFormat(DATETIME_FORMAT_STR, Locale.ENGLISH)
/**
* Tags that contain offset markers. These are included in the banned
* defines.
*/
private val sOffsetTags = HashSet<Short>().apply {
add(getTrueTagKey(TAG_GPS_IFD))
add(getTrueTagKey(TAG_EXIF_IFD))
add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT))
add(getTrueTagKey(TAG_INTEROPERABILITY_IFD))
add(getTrueTagKey(TAG_STRIP_OFFSETS))
}
/**
* Tags with definitions that cannot be overridden (banned defines).
*/
var sBannedDefines = HashSet(sOffsetTags).apply {
add(getTrueTagKey(TAG_NULL))
add(getTrueTagKey(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
add(getTrueTagKey(TAG_STRIP_BYTE_COUNTS))
}
/**
* Returns true if tag TID is one of the following: [.TAG_EXIF_IFD],
* [.TAG_GPS_IFD], [.TAG_JPEG_INTERCHANGE_FORMAT],
* [.TAG_STRIP_OFFSETS], [.TAG_INTEROPERABILITY_IFD]
*
*
* Note: defining tags with these TID's is disallowed.
*
* @param tag a tag's TID (can be obtained from a defined tag constant with
* [.getTrueTagKey]).
* @return true if the TID is that of an offset tag.
*/
fun isOffsetTag(tag : Short) : Boolean =
sOffsetTags.contains(tag)
/**
* Returns the Orientation ExifTag value for a given number of degrees.
*
* @param degreesArg the amount an image is rotated in degrees.
*/
fun getOrientationValueForRotation(degreesArg : Int) : Short {
var degrees = degreesArg
degrees %= 360
if(degrees < 0) {
degrees += 360
}
return when {
degrees < 90 -> Orientation.TOP_LEFT // 0 degrees
degrees < 180 -> Orientation.RIGHT_TOP // 90 degrees cw
degrees < 270 -> Orientation.BOTTOM_LEFT // 180 degrees
else -> Orientation.RIGHT_BOTTOM // 270 degrees cw
}
}
/**
* Returns the rotation degrees corresponding to an ExifTag Orientation
* value.
*
* @param orientation the ExifTag Orientation value.
*/
fun getRotationForOrientationValue(orientation : Short) : Int =
when(orientation) {
Orientation.TOP_LEFT -> 0
Orientation.RIGHT_TOP -> 90
Orientation.BOTTOM_LEFT -> 180
Orientation.RIGHT_BOTTOM -> 270
else -> 0
}
/**
* 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
*/
fun convertLatOrLongToDouble(coordinate : Array<Rational>, reference : String) : Double {
val degrees = coordinate[0].toDouble()
val minutes = coordinate[1].toDouble()
val seconds = coordinate[2].toDouble()
val result = degrees + minutes / 60.0 + seconds / 3600.0
return when {
reference.startsWith("S") || reference.startsWith("W") -> - result
else -> result
}
}
fun getAllowedIfdsFromInfo(info : Int) : IntArray? {
val ifdFlags = getAllowedIfdFlagsFromInfo(info)
val ifds = IfdData.list
val l = ArrayList<Int>()
for(i in 0 until IfdData.TYPE_IFD_COUNT) {
val flag = ifdFlags shr i and 1
if(flag == 1) {
l.add(ifds[i])
}
}
if(l.size <= 0) {
return null
}
val ret = IntArray(l.size)
var j = 0
for(i in l) {
ret[j ++] = i
}
return ret
}
@Throws(IOException::class)
private fun writeExif_internal(
input : InputStream,
output : OutputStream,
exifData : ExifData
) : Int {
// Log.i( TAG, "writeExif_internal" );
// 1. read the output file first
val src_exif = ExifInterface()
src_exif.readExif(input, 0)
// 4. Create the destination outputstream
// 5. write headers
output.write(0xFF)
output.write(JpegHeader.TAG_SOI)
val sections = src_exif.mData.sections
// 6. write all the sections from the srcFilename
if(sections.firstOrNull()?.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
val eo = ExifOutputStream(src_exif, exifData)
eo.writeExifData(output)
// 6.2 write all the sections except for the SOS ( start of scan )
sections.forEach {
// Log.v( TAG, "writing section.. " + String.format( "0x%2X", current.type ) );
output.write(0xFF)
output.write(it.type)
output.write(it.data)
}
// 6.3 write the last SOS marker
val current = sections[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
}
/**
* Returns the default IFD for a tag constant.
*/
fun getTrueIfd(tag : Int) : Int = tag.ushr(16)
/**
* Returns the TID for a tag constant.
*/
fun getTrueTagKey(tag : Int) : Short = tag.toShort()
private fun getFlagsFromAllowedIfds(allowedIfds : IntArray) : Int {
if(allowedIfds.isEmpty()) return 0
var flags = 0
val ifds = IfdData.list
for(i in 0 until IfdData.TYPE_IFD_COUNT) {
for(j in allowedIfds) {
if(ifds[i] == j) {
flags = flags or (1 shl i)
break
}
}
}
return flags
}
private fun getComponentCountFromInfo(info : Int) : Int = info and 0x0ffff
private fun getTypeFromInfo(info : Int) : Short = (info shr 16 and 0x0ff).toShort()
/**
* Returns the constant representing a tag with a given TID and default IFD.
*/
fun defineTag(ifdId : Int, tagId : Short) : Int = tagId or (ifdId shl 16)
private fun convertRationalLatLonToString(
coord : Array<Rational>,
refArg : String
) : String? {
return try {
var ref = refArg
val degrees = coord[0].toDouble()
val minutes = coord[1].toDouble()
val seconds = coord[2].toDouble()
ref = ref.substring(0, 1)
String.format(
Locale.ENGLISH,
"%1$.0f° %2$.0f' %3$.0f\" %4\$s",
degrees,
minutes,
seconds,
ref.toUpperCase(Locale.ENGLISH)
)
} catch(ex : Throwable) {
ex.printStackTrace()
null
}
}
/**
* Given an exif date time, like [.TAG_DATE_TIME] or [.TAG_DATE_TIME_DIGITIZED]
* returns a java Date object
*
* @param dateTimeString one of the value of [.TAG_DATE_TIME] or [.TAG_DATE_TIME_DIGITIZED]
* @param timeZone the target timezone
* @return the parsed date
*/
fun getDateTime(dateTimeString : String, timeZone : TimeZone) : Date? {
return try {
val formatter = SimpleDateFormat(DATETIME_FORMAT_STR, Locale.ENGLISH)
formatter.timeZone = timeZone
formatter.parse(dateTimeString)
} catch(e : Throwable) {
e.printStackTrace()
null
}
}
fun isIfdAllowed(info : Int, ifd : Int) : Boolean {
val ifdFlags = getAllowedIfdFlagsFromInfo(info)
IfdData.list.forEachIndexed { itemIndex, itemId ->
if(ifd == itemId && ifdFlags shr itemIndex and 1 == 1) return true
}
return false
}
private fun getAllowedIfdFlagsFromInfo(info : Int) : Int = info.ushr(24)
private fun toExifLatLong(valueArg : Double) : Array<Rational> {
// convert to the format dd/1 mm/1 ssss/100
var value = abs(valueArg)
val degrees = value
value = (value - degrees) * 60
val minutes = value
value = (value - minutes) * 6000
val seconds = value
return arrayOf(
Rational(degrees.toLong(), 1),
Rational(minutes.toLong(), 1),
Rational(seconds.toLong(), 100)
)
}
fun toBitArray(value : Short) : ByteArray {
val result = ByteArray(16)
for(i in 0 .. 15) {
result[15 - i] = (value shr i and 1).toByte()
}
return result
}
infix fun Short.shl(bits : Int) : Int = (this.toInt() and 0xffff) shl bits
infix fun Short.shr(bits : Int) : Int = (this.toInt() and 0xffff) shr bits
infix fun Short.or(bits : Int) : Int = (this.toInt() and 0xffff) or bits
private fun SparseIntArray.initTagInfo() : SparseIntArray {
var f : Int
/*
* 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
f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_0, IfdData.TYPE_IFD_1)) shl 24
put(TAG_MAKE, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_IMAGE_WIDTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_IMAGE_LENGTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_BITS_PER_SAMPLE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 3)
put(TAG_COMPRESSION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_PHOTOMETRIC_INTERPRETATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_ORIENTATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SAMPLES_PER_PIXEL, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_PLANAR_CONFIGURATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_Y_CB_CR_SUB_SAMPLING, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 2)
put(TAG_Y_CB_CR_POSITIONING, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_X_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_Y_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_RESOLUTION_UNIT, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_STRIP_OFFSETS, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16))
put(TAG_ROWS_PER_STRIP, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_STRIP_BYTE_COUNTS, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16))
put(TAG_TRANSFER_FUNCTION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 3 * 256)
put(TAG_WHITE_POINT, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 2)
put(TAG_PRIMARY_CHROMATICITIES, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 6)
put(TAG_Y_CB_CR_COEFFICIENTS, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 3)
put(TAG_REFERENCE_BLACK_WHITE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 6)
put(TAG_DATE_TIME, f or (ExifTag.TYPE_ASCII shl 16) or 20)
put(TAG_IMAGE_DESCRIPTION, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_MODEL, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_SOFTWARE, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_ARTIST, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_COPYRIGHT, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_EXIF_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_GPS_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
// IFD1 tags
f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_1)) shl 24
put(TAG_JPEG_INTERCHANGE_FORMAT, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_JPEG_INTERCHANGE_FORMAT_LENGTH, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
// Exif tags
f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_EXIF)) shl 24
put(TAG_EXIF_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
put(TAG_FLASHPIX_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
put(TAG_COLOR_SPACE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_COMPONENTS_CONFIGURATION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
put(TAG_COMPRESSED_BITS_PER_PIXEL, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_PIXEL_X_DIMENSION, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_PIXEL_Y_DIMENSION, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
put(TAG_MAKER_NOTE, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_USER_COMMENT, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_RELATED_SOUND_FILE, f or (ExifTag.TYPE_ASCII shl 16) or 13)
put(TAG_DATE_TIME_ORIGINAL, f or (ExifTag.TYPE_ASCII shl 16) or 20)
put(TAG_DATE_TIME_DIGITIZED, f or (ExifTag.TYPE_ASCII shl 16) or 20)
put(TAG_SUB_SEC_TIME, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_SUB_SEC_TIME_ORIGINAL, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_SUB_SEC_TIME_DIGITIZED, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_IMAGE_UNIQUE_ID, f or (ExifTag.TYPE_ASCII shl 16) or 33)
put(TAG_LENS_SPECS, f or (ExifTag.TYPE_RATIONAL shl 16) or 4)
put(TAG_LENS_MAKE, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_LENS_MODEL, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_SENSITIVITY_TYPE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_EXPOSURE_TIME, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_F_NUMBER, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_EXPOSURE_PROGRAM, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SPECTRAL_SENSITIVITY, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_ISO_SPEED_RATINGS, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16))
put(TAG_OECF, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_SHUTTER_SPEED_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
put(TAG_APERTURE_VALUE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_BRIGHTNESS_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
put(TAG_EXPOSURE_BIAS_VALUE, f or (ExifTag.TYPE_RATIONAL shl 16) or 1)
put(TAG_MAX_APERTURE_VALUE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_SUBJECT_DISTANCE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_METERING_MODE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_LIGHT_SOURCE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_FLASH, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_FOCAL_LENGTH, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_SUBJECT_AREA, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16))
put(TAG_FLASH_ENERGY, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_SPATIAL_FREQUENCY_RESPONSE, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_FOCAL_PLANE_X_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_FOCAL_PLANE_Y_RESOLUTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_FOCAL_PLANE_RESOLUTION_UNIT, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SUBJECT_LOCATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 2)
put(TAG_EXPOSURE_INDEX, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_SENSING_METHOD, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_FILE_SOURCE, f or (ExifTag.TYPE_UNDEFINED shl 16) or 1)
put(TAG_SCENE_TYPE, f or (ExifTag.TYPE_UNDEFINED shl 16) or 1)
put(TAG_CFA_PATTERN, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_CUSTOM_RENDERED, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_EXPOSURE_MODE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_WHITE_BALANCE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_DIGITAL_ZOOM_RATIO, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_FOCAL_LENGTH_IN_35_MM_FILE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SCENE_CAPTURE_TYPE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_GAIN_CONTROL, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_CONTRAST, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SATURATION, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_SHARPNESS, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_DEVICE_SETTING_DESCRIPTION, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_SUBJECT_DISTANCE_RANGE, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 1)
put(TAG_INTEROPERABILITY_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1)
// GPS tag
f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_GPS)) shl 24
put(TAG_GPS_VERSION_ID, f or (ExifTag.TYPE_UNSIGNED_BYTE shl 16) or 4)
put(TAG_GPS_LATITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_LONGITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_LATITUDE, f or (ExifTag.TYPE_RATIONAL shl 16) or 3)
put(TAG_GPS_LONGITUDE, f or (ExifTag.TYPE_RATIONAL shl 16) or 3)
put(TAG_GPS_ALTITUDE_REF, f or (ExifTag.TYPE_UNSIGNED_BYTE shl 16) or 1)
put(TAG_GPS_ALTITUDE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_TIME_STAMP, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 3)
put(TAG_GPS_SATTELLITES, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_GPS_STATUS, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_MEASURE_MODE, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_DOP, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_SPEED_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_SPEED, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_TRACK_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_TRACK, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_IMG_DIRECTION_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_IMG_DIRECTION, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_MAP_DATUM, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_GPS_DEST_LATITUDE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_DEST_LATITUDE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_DEST_BEARING_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_DEST_BEARING, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_DEST_DISTANCE_REF, f or (ExifTag.TYPE_ASCII shl 16) or 2)
put(TAG_GPS_DEST_DISTANCE, f or (ExifTag.TYPE_UNSIGNED_RATIONAL shl 16) or 1)
put(TAG_GPS_PROCESSING_METHOD, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_GPS_AREA_INFORMATION, f or (ExifTag.TYPE_UNDEFINED shl 16))
put(TAG_GPS_DATE_STAMP, f or (ExifTag.TYPE_ASCII shl 16) or 11)
put(TAG_GPS_DIFFERENTIAL, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 11)
// Interoperability tag
f = getFlagsFromAllowedIfds(intArrayOf(IfdData.TYPE_IFD_INTEROPERABILITY)) shl 24
put(TAG_INTEROPERABILITY_INDEX, f or (ExifTag.TYPE_ASCII shl 16))
put(TAG_INTEROP_VERSION, f or (ExifTag.TYPE_UNDEFINED shl 16) or 4)
return this
}
}
}