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

843 lines
25 KiB
Kotlin

/*
* Copyright (C) 2012 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 java.nio.charset.Charset
import java.text.SimpleDateFormat
import java.util.*
/**
* This class stores information of an EXIF tag. For more information about
* defined EXIF tags, please read the Jeita EXIF 2.2 standard. Tags should be
* instantiated using [ExifInterface.buildTag].
*
* @see ExifInterface
*/
// Use builtTag in ExifInterface instead of constructor.
@Suppress("unused")
open class ExifTag internal constructor(
// Exif TagId. the TID of this tag.
val tagId : Short,
// Exif Tag Type. the data type of this tag
/*
* @see .TYPE_ASCII
* @see .TYPE_LONG
* @see .TYPE_RATIONAL
* @see .TYPE_UNDEFINED
* @see .TYPE_UNSIGNED_BYTE
* @see .TYPE_UNSIGNED_LONG
* @see .TYPE_UNSIGNED_RATIONAL
* @see .TYPE_UNSIGNED_SHORT
*/
val dataType : Short,
componentCount : Int,
// The ifd that this tag should be put in. the ID of the IFD this tag belongs to.
/*
* @see IfdData.TYPE_IFD_0
* @see IfdData.TYPE_IFD_1
* @see IfdData.TYPE_IFD_EXIF
* @see IfdData.TYPE_IFD_GPS
* @see IfdData.TYPE_IFD_INTEROPERABILITY
*/
var ifd : Int,
// If tag has defined count
private var mHasDefinedDefaultComponentCount : Boolean
) {
// Actual data count in tag (should be number of elements in value array)
/**
* Gets the component count of this tag.
*/
// TODO: fix integer overflows with this
var componentCount : Int = componentCount
private set
// The value (array of elements of type Tag Type)
private var mValue : Any? = null
// Value offset in exif header. the offset of this tag.
// This is only valid if this data size > 4 and contains an offset to the location of the actual value.
var offset : Int = 0
// the total data size in bytes of the value of this tag.
val dataSize : Int
get() = componentCount * getElementSize(dataType)
/**
* Returns true if this ExifTag contains value; otherwise, this tag will
* contain an offset value that is determined when the tag is written.
*/
val hasValue :Boolean
get() = mValue != null
/**
* Gets the value as a byte array. This method should be used for tags of
* type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
*
* @return the value as a byte array, or null if the tag's value does not
* exist or cannot be converted to a byte array.
*/
val valueAsBytes : ByteArray?
get() = mValue as? ByteArray
/**
* Gets the value as an array of longs. This method should be used for tags
* of type [.TYPE_UNSIGNED_LONG].
*
* @return the value as as an array of longs, or null if the tag's value
* does not exist or cannot be converted to an array of longs.
*/
val valueAsLongs : LongArray?
get() = mValue as? LongArray
/**
* Gets the value as an array of Rationals. This method should be used for
* tags of type [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
*
* @return the value as as an array of Rationals, or null if the tag's value
* does not exist or cannot be converted to an array of Rationals.
*/
@Suppress("UNCHECKED_CAST")
val valueAsRationals : Array<Rational>?
get() = mValue as? Array<Rational>
/**
* Gets the value as an array of ints. This method should be used for tags
* of type [.TYPE_UNSIGNED_SHORT], [.TYPE_UNSIGNED_LONG].
*
* @return the value as as an array of ints, or null if the tag's value does
* not exist or cannot be converted to an array of ints.
*/
// Truncates
val valueAsInts : IntArray?
get() = when(val v = mValue) {
is LongArray -> IntArray(v.size) { v[it].toInt() }
else -> null
}
/**
* Gets the value as a String. This method should be used for tags of type
* [.TYPE_ASCII].
*
* @return the value as a String, or null if the tag's value does not exist
* or cannot be converted to a String.
*/
val valueAsString : String?
get() = when(val v = mValue) {
is String -> v
is ByteArray -> String(v, US_ASCII)
else -> null
}
/**
* Gets the [.TYPE_ASCII] data.
*
* @throws IllegalArgumentException If the type is NOT
* [.TYPE_ASCII].
*/
protected val string : String
get() = valueAsString !!
/*
* Get the converted ascii byte. Used by ExifOutputStream.
*/
val stringByte : ByteArray?
get() = mValue as? ByteArray
/**
* Sets the component count of this tag. Call this function before
* setValue() if the length of value does not match the component count.
*/
fun forceSetComponentCount(count : Int) {
componentCount = count
}
/**
* Sets integer values into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_SHORT]. This method will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_SHORT],
* [.TYPE_UNSIGNED_LONG], or [.TYPE_LONG].
* * The value overflows.
* * The value.length does NOT match the component count in the definition
* for this tag.
*
*/
fun setValue(value : IntArray) : Boolean {
if(checkBadComponentCount(value.size)) {
return false
}
if(dataType != TYPE_UNSIGNED_SHORT && dataType != TYPE_LONG &&
dataType != TYPE_UNSIGNED_LONG) {
return false
}
if(dataType == TYPE_UNSIGNED_SHORT && checkOverflowForUnsignedShort(value)) {
return false
} else if(dataType == TYPE_UNSIGNED_LONG && checkOverflowForUnsignedLong(value)) {
return false
}
val data = LongArray(value.size)
for(i in value.indices) {
data[i] = value[i].toLong()
}
mValue = data
componentCount = value.size
return true
}
/**
* Sets integer value into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_SHORT], or [.TYPE_LONG]. This method
* will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_SHORT],
* [.TYPE_UNSIGNED_LONG], or [.TYPE_LONG].
* * The value overflows.
* * The component count in the definition of this tag is not 1.
*
*/
fun setValue(value : Int) = setValue(intArrayOf(value))
/**
* Sets long values into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_LONG]. This method will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_LONG].
* * The value overflows.
* * The value.length does NOT match the component count in the definition
* for this tag.
*
*/
fun setValue(value : LongArray) : Boolean {
if(checkBadComponentCount(value.size) || dataType != TYPE_UNSIGNED_LONG) {
return false
}
if(checkOverflowForUnsignedLong(value)) {
return false
}
mValue = value
componentCount = value.size
return true
}
/**
* Sets long values into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_LONG]. This method will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_LONG].
* * The value overflows.
* * The component count in the definition for this tag is not 1.
*
*/
fun setValue(value : Long) = setValue(longArrayOf(value))
/**
* Sets Rational values into this tag. This method should be used for tags
* of type [.TYPE_UNSIGNED_RATIONAL], or [.TYPE_RATIONAL]. This
* method will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_RATIONAL]
* or [.TYPE_RATIONAL].
* * The value overflows.
* * The value.length does NOT match the component count in the definition
* for this tag.
*
*
* @see Rational
*/
fun setValue(value : Array<Rational>) : Boolean {
if(checkBadComponentCount(value.size)) {
return false
}
if(dataType != TYPE_UNSIGNED_RATIONAL && dataType != TYPE_RATIONAL) {
return false
}
if(dataType == TYPE_UNSIGNED_RATIONAL && checkOverflowForUnsignedRational(value)) {
return false
} else if(dataType == TYPE_RATIONAL && checkOverflowForRational(value)) {
return false
}
mValue = value
componentCount = value.size
return true
}
/**
* Sets a Rational value into this tag. This method should be used for tags
* of type [.TYPE_UNSIGNED_RATIONAL], or [.TYPE_RATIONAL]. This
* method will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_RATIONAL]
* or [.TYPE_RATIONAL].
* * The value overflows.
* * The component count in the definition for this tag is not 1.
*
*
* @see Rational
*/
fun setValue(value : Rational) =setValue(arrayOf(value))
/**
* Sets byte values into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_BYTE] or [.TYPE_UNDEFINED]. This method
* will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_BYTE] or
* [.TYPE_UNDEFINED] .
* * The length does NOT match the component count in the definition for
* this tag.
*
*/
@JvmOverloads
fun setValue(value : ByteArray, offset : Int = 0, length : Int = value.size) : Boolean {
if(checkBadComponentCount(length)) {
return false
}
if(dataType != TYPE_UNSIGNED_BYTE && dataType != TYPE_UNDEFINED) {
return false
}
componentCount = length
mValue = ByteArray(length).also {
System.arraycopy(value, offset, it, 0, length)
}
return true
}
/**
* Sets byte value into this tag. This method should be used for tags of
* type [.TYPE_UNSIGNED_BYTE] or [.TYPE_UNDEFINED]. This method
* will fail if:
*
* * The component type of this tag is not [.TYPE_UNSIGNED_BYTE] or
* [.TYPE_UNDEFINED] .
* * The component count in the definition for this tag is not 1.
*
*/
fun setValue(value : Byte) = setValue(byteArrayOf(value))
/**
* Sets the value for this tag using an appropriate setValue method for the
* given object. This method will fail if:
*
* * The corresponding setValue method for the class of the object passed
* in would fail.
* * There is no obvious way to cast the object passed in into an EXIF tag
* type.
*
*/
inline fun <reified T : Any> setValueAny(obj : T) : Boolean {
when(obj) {
// null -> return false
is String -> return setValue(obj)
is ByteArray -> return setValue(obj)
is IntArray -> return setValue(obj)
is LongArray -> return setValue(obj)
is Rational -> return setValue(obj)
is Byte -> return setValue(obj.toByte())
is Short -> return setValue(obj.toInt() and 0x0ffff)
is Int -> return setValue(obj.toInt())
is Long -> return setValue(obj.toLong())
else -> {
@Suppress("UNCHECKED_CAST")
val ra = obj as? Array<Rational>
if(ra != null) return setValue(ra)
// Nulls in this array are treated as zeroes.
@Suppress("UNCHECKED_CAST")
val sa = obj as? Array<Short?>
if(sa != null) return setValue(IntArray(sa.size) {
(sa[it]?.toInt() ?: 0) and 0xffff
})
// Nulls in this array are treated as zeroes.
@Suppress("UNCHECKED_CAST")
val ia = obj as? Array<Int?>
if(ia != null) return setValue(IntArray(ia.size) { ia[it] ?: 0 })
// Nulls in this array are treated as zeroes.
@Suppress("UNCHECKED_CAST")
val la = obj as? Array<Long?>
if(la != null) return setValue(LongArray(la.size) { la[it] ?: 0L })
// Nulls in this array are treated as zeroes.
@Suppress("UNCHECKED_CAST")
val ba = obj as? Array<Byte?>
if(ba != null) return setValue(ByteArray(ba.size) { ba[it] ?: 0 })
return false
}
}
}
/**
* Sets a timestamp to this tag. The method converts the timestamp with the
* format of "yyyy:MM:dd kk:mm:ss" and calls [.setValue]. This
* method will fail if the data type is not [.TYPE_ASCII] or the
* component count of this tag is not 20 or undefined.
*
* @param time the number of milliseconds since Jan. 1, 1970 GMT
* @return true on success
*/
fun setValueTime(time : Long) : Boolean {
// synchronized on TIME_FORMAT as SimpleDateFormat is not thread safe
synchronized(TIME_FORMAT) {
return setValue(TIME_FORMAT.format(Date(time)))
}
}
/**
* Sets a string value into this tag. This method should be used for tags of
* type [.TYPE_ASCII]. The string is converted to an ASCII string.
* Characters that cannot be converted are replaced with '?'. The length of
* the string must be equal to either (component count -1) or (component
* count). The final byte will be set to the string null terminator '\0',
* overwriting the last character in the string if the value.length is equal
* to the component count. This method will fail if:
*
* * The data type is not [.TYPE_ASCII] or [.TYPE_UNDEFINED].
* * The length of the string is not equal to (component count -1) or
* (component count) in the definition for this tag.
*
*/
fun setValue(value : String) : Boolean {
if(dataType != TYPE_ASCII && dataType != TYPE_UNDEFINED) {
return false
}
val buf = value.toByteArray(US_ASCII)
val finalBuf = when {
buf.isNotEmpty() -> when {
buf[buf.size - 1].toInt() == 0 || dataType == TYPE_UNDEFINED -> buf
else -> buf.copyOf(buf.size + 1)
}
dataType == TYPE_ASCII && componentCount == 1 -> byteArrayOf(0)
else -> buf
}
val count = finalBuf.size
if(checkBadComponentCount(count)) {
return false
}
componentCount = count
mValue = finalBuf
return true
}
private fun checkBadComponentCount(count : Int) : Boolean {
return mHasDefinedDefaultComponentCount && componentCount != count
}
/**
* Gets the value as a String. This method should be used for tags of type
* [.TYPE_ASCII].
*
* @param defaultValue the String to return if the tag's value does not
* exist or cannot be converted to a String.
* @return the tag's value as a String, or the defaultValue.
*/
fun getValueAsString(defaultValue : String) : String {
return valueAsString ?: defaultValue
}
/**
* Gets the value as a byte. If there are more than 1 bytes in this value,
* gets the first byte. This method should be used for tags of type
* [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
*
* @param defaultValue the byte to return if tag's value does not exist or
* cannot be converted to a byte.
* @return the tag's value as a byte, or the defaultValue.
*/
fun getValueAsByte(defaultValue : Byte) : Byte {
val array = valueAsBytes
return if(array?.isNotEmpty() == true) array[0] else defaultValue
}
/**
* Gets the value as a Rational. If there are more than 1 Rationals in this
* value, gets the first one. This method should be used for tags of type
* [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
*
* @param defaultValue the numerator of the Rational to return if tag's
* value does not exist or cannot be converted to a Rational (the
* denominator will be 1).
* @return the tag's value as a Rational, or the defaultValue.
*/
fun getValueAsRational(defaultValue : Long) : Rational =
getValueAsRational(Rational(defaultValue, 1))
/**
* Gets the value as a Rational. If there are more than 1 Rationals in this
* value, gets the first one. This method should be used for tags of type
* [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
*
* @param defaultValue the Rational to return if tag's value does not exist
* or cannot be converted to a Rational.
* @return the tag's value as a Rational, or the defaultValue.
*/
private fun getValueAsRational(defaultValue : Rational) : Rational {
val array = valueAsRationals
return if(array?.isNotEmpty() == true) array[0] else defaultValue
}
/**
* Gets the value as an int. If there are more than 1 ints in this value,
* gets the first one. This method should be used for tags of type
* [.TYPE_UNSIGNED_SHORT], [.TYPE_UNSIGNED_LONG].
*
* @param defaultValue the int to return if tag's value does not exist or
* cannot be converted to an int.
* @return the tag's value as a int, or the defaultValue.
*/
fun getValueAsInt(defaultValue : Int) : Int {
val array = valueAsInts
return if(array?.isNotEmpty() == true) array[0] else defaultValue
}
/**
* Gets the value or null if none exists. If there are more than 1 longs in
* this value, gets the first one. This method should be used for tags of
* type [.TYPE_UNSIGNED_LONG].
*
* @param defaultValue the long to return if tag's value does not exist or
* cannot be converted to a long.
* @return the tag's value as a long, or the defaultValue.
*/
fun getValueAsLong(defaultValue : Long) : Long {
val array = valueAsLongs
return if(array?.isNotEmpty() == true) array[0] else defaultValue
}
/**
* Gets the tag's value or null if none exists.
*/
fun getValue() : Any? {
return mValue
}
/**
* Gets a long representation of the value.
*
* @param defaultValue value to return if there is no value or value is a
* rational with a denominator of 0.
* @return the tag's value as a long, or defaultValue if no representation
* exists.
*/
fun forceGetValueAsLong(defaultValue : Long) : Long {
when(val v = mValue) {
is LongArray -> if(v.isNotEmpty()) return v[0]
is ByteArray -> if(v.isNotEmpty()) return v[0].toLong()
else -> {
val r = valueAsRationals
if(r?.isNotEmpty() == true && r[0].denominator != 0L) {
return r[0].toDouble().toLong()
}
}
}
return defaultValue
}
/**
* Gets the value for type [.TYPE_ASCII], [.TYPE_LONG],
* [.TYPE_UNDEFINED], [.TYPE_UNSIGNED_BYTE],
* [.TYPE_UNSIGNED_LONG], or [.TYPE_UNSIGNED_SHORT]. For
* [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL], call
* [.getRational] instead.
*
* @throws IllegalArgumentException if the data type is
* [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
*/
fun getValueAt(index : Int) : Long {
return when(val v = mValue) {
is LongArray -> v[index]
is ByteArray -> v[index].toLong()
else -> error(
"Cannot get integer value from ${convertTypeToString(dataType)}"
)
}
}
/**
* Gets the [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL] data.
*
* @throws IllegalArgumentException If the type is NOT
* [.TYPE_RATIONAL] or [.TYPE_UNSIGNED_RATIONAL].
*/
fun getRational(index : Int) : Rational? {
require(! (dataType != TYPE_RATIONAL && dataType != TYPE_UNSIGNED_RATIONAL)) {
"Cannot get RATIONAL value from " + convertTypeToString(dataType)
}
return valueAsRationals?.get(index)
}
/**
* Gets the [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE] data.
*
* @param buf the byte array in which to store the bytes read.
* @param offset the initial position in buffer to store the bytes.
* @param length the maximum number of bytes to store in buffer. If length >
* component count, only the valid bytes will be stored.
* @throws IllegalArgumentException If the type is NOT
* [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
*/
@JvmOverloads
fun getBytes(buf : ByteArray, offset : Int = 0, length : Int = buf.size) {
require(! (dataType != TYPE_UNDEFINED && dataType != TYPE_UNSIGNED_BYTE)) {
"Cannot get BYTE value from " + convertTypeToString(
dataType
)
}
System.arraycopy(
mValue !!,
0,
buf,
offset,
if(length > componentCount) componentCount else length
)
}
var hasDefinedCount : Boolean
get() = mHasDefinedDefaultComponentCount
set(value) {
mHasDefinedDefaultComponentCount = value
}
private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean =
null != value.find { it !in 0 .. UNSIGNED_SHORT_MAX }
private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean =
null != value.find { it !in 0 .. UNSIGNED_LONG_MAX }
private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean =
null != value.find { it < 0 }
private fun checkOverflowForUnsignedRational(value : Array<Rational>) : Boolean =
null != value.find { it.numerator !in 0 .. UNSIGNED_LONG_MAX || it.denominator !in 0 .. UNSIGNED_LONG_MAX }
private fun checkOverflowForRational(value : Array<Rational>) : Boolean =
null != value.find { it.numerator !in LONG_MIN .. LONG_MAX || it.denominator !in LONG_MIN .. LONG_MAX }
override fun hashCode() : Int {
var result = tagId.toInt()
result = 31 * result + dataType.toInt()
result = 31 * result + ifd
result = 31 * result + componentCount
result = 31 * result + offset
result = 31 * result + mHasDefinedDefaultComponentCount.hashCode()
result = 31 * result + (mValue?.hashCode() ?: 0)
return result
}
override fun equals(other : Any?) : Boolean {
if(other !is ExifTag) return false
if(other.tagId != this.tagId
|| other.componentCount != this.componentCount
|| other.dataType != this.dataType
) {
return false
}
val va = this.mValue
val vb = other.mValue
return when {
va == null -> vb == null
vb == null -> false
va is LongArray -> when(vb) {
is LongArray -> Arrays.equals(va, vb)
else -> false
}
va is ByteArray -> when(vb) {
is ByteArray -> Arrays.equals(va, vb)
else -> false
}
va is Array<*> && va.isArrayOf<Rational>() -> when {
vb is Array<*> && vb.isArrayOf<Rational>() -> Arrays.equals(va, vb)
else -> false
}
else -> va == vb
}
}
override fun toString() : String {
val strTagId = String.format("%04X", tagId)
return "tag id: $strTagId\nifd id: $ifd\ntype: ${convertTypeToString(dataType)}\ncount: $componentCount\noffset: $offset\nvalue: ${forceGetValueAsString()}\n"
}
/**
* Gets a string representation of the value.
*/
private fun forceGetValueAsString() : String {
when(val v = mValue) {
null -> return ""
is ByteArray -> return when(dataType) {
TYPE_ASCII -> String(v, US_ASCII)
else -> Arrays.toString(v)
}
is LongArray -> return when {
v.size == 1 -> v[0].toString()
else -> Arrays.toString(v)
}
is Array<*> -> return when {
v.size == 1 -> v[0]?.toString() ?: ""
else -> Arrays.toString(v)
}
else -> return v.toString()
}
}
companion object {
/**
* The BYTE type in the EXIF standard. An 8-bit unsigned integer.
*/
const val TYPE_UNSIGNED_BYTE : Short = 1
/**
* The ASCII type in the EXIF standard. An 8-bit byte containing one 7-bit
* ASCII code. The final byte is terminated with NULL.
*/
const val TYPE_ASCII : Short = 2
/**
* The SHORT type in the EXIF standard. A 16-bit (2-byte) unsigned integer
*/
const val TYPE_UNSIGNED_SHORT : Short = 3
/**
* The LONG type in the EXIF standard. A 32-bit (4-byte) unsigned integer
*/
const val TYPE_UNSIGNED_LONG : Short = 4
/**
* The RATIONAL type of EXIF standard. It consists of two LONGs. The first
* one is the numerator and the second one expresses the denominator.
*/
const val TYPE_UNSIGNED_RATIONAL : Short = 5
/**
* The UNDEFINED type in the EXIF standard. An 8-bit byte that can take any
* value depending on the field definition.
*/
const val TYPE_UNDEFINED : Short = 7
/**
* The SLONG type in the EXIF standard. A 32-bit (4-byte) signed integer
* (2's complement notation).
*/
const val TYPE_LONG : Short = 9
/**
* The SRATIONAL type of EXIF standard. It consists of two SLONGs. The first
* one is the numerator and the second one is the denominator.
*/
const val TYPE_RATIONAL : Short = 10
internal const val SIZE_UNDEFINED = 0
private val TYPE_TO_SIZE_MAP = IntArray(11)
private const val UNSIGNED_SHORT_MAX = 65535
private const val UNSIGNED_LONG_MAX = 4294967295L
private const val LONG_MAX = Integer.MAX_VALUE.toLong()
private const val LONG_MIN = Integer.MIN_VALUE.toLong()
init {
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_BYTE.toInt()] = 1
TYPE_TO_SIZE_MAP[TYPE_ASCII.toInt()] = 1
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_SHORT.toInt()] = 2
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_LONG.toInt()] = 4
TYPE_TO_SIZE_MAP[TYPE_UNSIGNED_RATIONAL.toInt()] = 8
TYPE_TO_SIZE_MAP[TYPE_UNDEFINED.toInt()] = 1
TYPE_TO_SIZE_MAP[TYPE_LONG.toInt()] = 4
TYPE_TO_SIZE_MAP[TYPE_RATIONAL.toInt()] = 8
}
private val TIME_FORMAT = SimpleDateFormat("yyyy:MM:dd kk:mm:ss", Locale.ENGLISH)
private val US_ASCII = Charset.forName("US-ASCII")
/**
* Returns true if the given IFD is a valid IFD.
*/
fun isValidIfd(ifdId : Int) : Boolean =
when(ifdId) {
IfdData.TYPE_IFD_0,
IfdData.TYPE_IFD_1,
IfdData.TYPE_IFD_EXIF,
IfdData.TYPE_IFD_INTEROPERABILITY,
IfdData.TYPE_IFD_GPS -> true
else -> false
}
/**
* Returns true if a given type is a valid tag type.
*/
fun isValidType(type : Short) : Boolean =
when(type) {
TYPE_UNSIGNED_BYTE,
TYPE_ASCII,
TYPE_UNSIGNED_SHORT,
TYPE_UNSIGNED_LONG,
TYPE_UNSIGNED_RATIONAL,
TYPE_UNDEFINED,
TYPE_LONG,
TYPE_RATIONAL -> true
else -> false
}
/**
* Gets the element size of the given data type in bytes.
*
* @see .TYPE_ASCII
* @see .TYPE_LONG
* @see .TYPE_RATIONAL
* @see .TYPE_UNDEFINED
* @see .TYPE_UNSIGNED_BYTE
* @see .TYPE_UNSIGNED_LONG
* @see .TYPE_UNSIGNED_RATIONAL
* @see .TYPE_UNSIGNED_SHORT
*/
fun getElementSize(type : Short) : Int = TYPE_TO_SIZE_MAP[type.toInt()]
private fun convertTypeToString(type : Short) : String =
when(type) {
TYPE_UNSIGNED_BYTE -> "UNSIGNED_BYTE"
TYPE_ASCII -> "ASCII"
TYPE_UNSIGNED_SHORT -> "UNSIGNED_SHORT"
TYPE_UNSIGNED_LONG -> "UNSIGNED_LONG"
TYPE_UNSIGNED_RATIONAL -> "UNSIGNED_RATIONAL"
TYPE_UNDEFINED -> "UNDEFINED"
TYPE_LONG -> "LONG"
TYPE_RATIONAL -> "RATIONAL"
else -> ""
}
}
}