diff --git a/.idea/dictionaries/tateisu.xml b/.idea/dictionaries/tateisu.xml index 80706fab..d171d3b2 100644 --- a/.idea/dictionaries/tateisu.xml +++ b/.idea/dictionaries/tateisu.xml @@ -51,6 +51,8 @@ idempotency ihdr infos + iptc + jfif kapt kddi kenglxn diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ByteBufferInputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ByteBufferInputStream.kt deleted file mode 100644 index 6ade7d36..00000000 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ByteBufferInputStream.kt +++ /dev/null @@ -1,38 +0,0 @@ -///* -// * 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.io.InputStream -//import java.nio.ByteBuffer -//import kotlin.math.min -// -//internal class ByteBufferInputStream(private val mBuf : ByteBuffer) : InputStream() { -// -// override fun read() : Int = when { -// ! mBuf.hasRemaining() -> - 1 -// else -> mBuf.get().toInt() and 0xFF -// } -// -// override fun read(bytes : ByteArray, off : Int, len : Int) : Int { -// if(! mBuf.hasRemaining()) return - 1 -// val willRead = min(len, mBuf.remaining()) -// mBuf.get(bytes, off, willRead) -// return willRead -// } -//} diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifData.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifData.kt index 89b55e18..76ff143d 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifData.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifData.kt @@ -17,6 +17,7 @@ package it.sephiroth.android.library.exif2 import android.util.Log +import it.sephiroth.android.library.exif2.utils.notEmpty import java.io.UnsupportedEncodingException import java.nio.ByteOrder @@ -33,94 +34,74 @@ import java.util.Arrays * @see IfdData */ @Suppress("unused") -internal open class ExifData( val byteOrder : ByteOrder ) { +internal class ExifData( + val byteOrder : ByteOrder = ExifInterface.DEFAULT_BYTE_ORDER, + val sections : List = ArrayList(), + val mUncompressedDataPosition : Int = 0, + val qualityGuess : Int = 0, + val jpegProcess : Short = 0 +) { - var sections : List? = null - private val mIfdDatas = arrayOfNulls(IfdId.TYPE_IFD_COUNT) - /** - * Gets the compressed thumbnail. Returns null if there is no compressed - * thumbnail. - * - * @see .hasCompressedThumbnail - */ - /** - * Sets the compressed thumbnail. - */ - var compressedThumbnail : ByteArray? = null - private val mStripBytes = ArrayList() - var qualityGuess = 0 private var imageLength = - 1 private var imageWidth = - 1 - var jpegProcess : Short = 0 - var mUncompressedDataPosition = 0 - /** - * Gets the strip count. - */ + private val mIfdDatas = arrayOfNulls(IfdData.TYPE_IFD_COUNT) + + // the compressed thumbnail. + // null if there is no compressed thumbnail. + var compressedThumbnail : ByteArray? = null + + private val mStripBytes = ArrayList() val stripCount : Int get() = mStripBytes.size - /** - * Decodes the user comment tag into string as specified in the EXIF - * standard. Returns null if decoding failed. - */ + // Decodes the user comment tag into string as specified in the EXIF standard. + // Returns null if decoding failed. val userComment : String? get() { - val ifdData = mIfdDatas[IfdId.TYPE_IFD_0] ?: return null + + val ifdData = mIfdDatas[IfdData.TYPE_IFD_0] + ?: return null + val tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT)) ?: return null - if(tag.componentCount < 8) { + + if(tag.componentCount < 8) return null - } - - val buf = ByteArray(tag.componentCount) - tag.getBytes(buf) - - val code = ByteArray(8) - System.arraycopy(buf, 0, code, 0, 8) return try { - when { - code.contentEquals(USER_COMMENT_ASCII) -> String( - buf, - 8, - buf.size - 8, - Charsets.US_ASCII - ) - code.contentEquals(USER_COMMENT_JIS) -> String( - buf, - 8, - buf.size - 8, - Charset.forName("EUC-JP") - ) - code.contentEquals(USER_COMMENT_UNICODE) -> String( - buf, - 8, - buf.size - 8, - Charsets.UTF_16 - ) + + val buf = ByteArray(tag.componentCount) + tag.getBytes(buf) + + val code = ByteArray(8) + System.arraycopy(buf, 0, code, 0, 8) + + val charset = when { + code.contentEquals(USER_COMMENT_ASCII) -> Charsets.US_ASCII + code.contentEquals(USER_COMMENT_JIS) -> eucJp + code.contentEquals(USER_COMMENT_UNICODE) -> Charsets.UTF_16 else -> null } + if(charset == null) null else String(buf, 8, buf.size - 8, charset) } catch(e : UnsupportedEncodingException) { Log.w(TAG, "Failed to decode the user comment") null } } - /** - * Returns a list of all [ExifTag]s in the ExifData or null if there - * are none. - */ - val allTags : List? - get() { - val ret = ArrayList() - mIfdDatas.forEach { it?.allTags?.forEach { tag -> ret.add(tag) } } - return if(ret.isEmpty()) null else ret - } + // list of all [ExifTag]s in the ExifData + // or null if there are none. + val allTags : List + get() = ArrayList() + .apply { mIfdDatas.forEach { if(it != null) addAll(it.allTagsCollection) } } val imageSize : IntArray get() = intArrayOf(imageWidth, imageLength) + val stripList : List? + get() = mStripBytes.filterNotNull().notEmpty() + /** * Returns true it this header contains a compressed thumbnail. */ @@ -166,41 +147,36 @@ internal open class ExifData( val byteOrder : ByteOrder ) { * Returns the tag with a given TID in the given IFD if the tag exists. * Otherwise returns null. */ - fun getTag(tag : Short, ifd : Int) : ExifTag? { - val ifdData = mIfdDatas[ifd] - return ifdData?.getTag(tag) - } + fun getTag(tag : Short, ifd : Int) : ExifTag? = + mIfdDatas[ifd]?.getTag(tag) /** * Adds the given ExifTag to its default IFD and returns an existing ExifTag * with the same TID or null if none exist. */ - fun addTag(tag : ExifTag?) : ExifTag? { - if(tag != null) { - val ifd = tag.ifd - return addTag(tag, ifd) + fun addTag(tag : ExifTag?) : ExifTag? = + when(tag) { + null -> null + else -> addTag(tag, tag.ifd) } - return null - } /** * Adds the given ExifTag to the given IFD and returns an existing ExifTag * with the same TID or null if none exist. */ - private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? { - if(tag != null && ExifTag.isValidIfd(ifdId)) { - val ifdData = getOrCreateIfdData(ifdId) - return ifdData.setTag(tag) + private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? = + when { + tag == null -> null + ! ExifTag.isValidIfd(ifdId) -> null + else -> getOrCreateIfdData(ifdId).setTag(tag) } - return null - } /** * Returns the [IfdData] object corresponding to a given IFD or * generates one if none exist. */ private fun getOrCreateIfdData(ifdId : Int) : IfdData { - var ifdData : IfdData? = mIfdDatas[ifdId] + var ifdData = mIfdDatas[ifdId] if(ifdData == null) { ifdData = IfdData(ifdId) mIfdDatas[ifdId] = ifdData @@ -211,9 +187,9 @@ internal open class ExifData( val byteOrder : ByteOrder ) { /** * Removes the thumbnail and its related tags. IFD1 will be removed. */ - protected fun removeThumbnailData() { + fun removeThumbnailData() { clearThumbnailAndStrips() - mIfdDatas[IfdId.TYPE_IFD_1] = null + mIfdDatas[IfdData.TYPE_IFD_1] = null } fun clearThumbnailAndStrips() { @@ -233,17 +209,8 @@ internal open class ExifData( val byteOrder : ByteOrder ) { * Returns a list of all [ExifTag]s in a given IFD or null if there * are none. */ - fun getAllTagsForIfd(ifd : Int) : List? { - val d = mIfdDatas[ifd] ?: return null - val tags = d.allTags - val ret = ArrayList(tags.size) - for(t in tags) { - ret.add(t) - } - return if(ret.size == 0) { - null - } else ret - } + fun getAllTagsForIfd(ifd : Int) : List? = + mIfdDatas[ifd]?.allTagsCollection?.notEmpty()?.toList() // Returns a list of all [ExifTag]s with a given TID // or null if there are none. @@ -280,7 +247,7 @@ internal open class ExifData( val byteOrder : ByteOrder ) { } } - for(i in 0 until IfdId.TYPE_IFD_COUNT) { + for(i in 0 until IfdData.TYPE_IFD_COUNT) { val ifd1 = other.getIfdData(i) val ifd2 = getIfdData(i) if(ifd1 != ifd2) return false @@ -294,10 +261,9 @@ internal open class ExifData( val byteOrder : ByteOrder ) { * Returns the [IfdData] object corresponding to a given IFD if it * exists or null. */ - fun getIfdData(ifdId : Int) : IfdData? { - return if(ExifTag.isValidIfd(ifdId)) { - mIfdDatas[ifdId] - } else null + fun getIfdData(ifdId : Int) = when { + ! ExifTag.isValidIfd(ifdId) -> null + else -> mIfdDatas[ifdId] } fun setImageSize(imageWidth : Int, imageLength : Int) { @@ -307,7 +273,7 @@ internal open class ExifData( val byteOrder : ByteOrder ) { override fun hashCode() : Int { var result = byteOrder.hashCode() - result = 31 * result + (sections?.hashCode() ?: 0) + result = 31 * result + (sections.hashCode()) result = 31 * result + mIfdDatas.contentHashCode() result = 31 * result + (compressedThumbnail?.contentHashCode() ?: 0) result = 31 * result + mStripBytes.hashCode() @@ -330,5 +296,8 @@ internal open class ExifData( val byteOrder : ByteOrder ) { private val USER_COMMENT_UNICODE = byteArrayOf(0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00) + + private val eucJp = Charset.forName("EUC-JP") + } } diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInterface.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInterface.kt index 435e031d..3e73fe06 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInterface.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifInterface.kt @@ -49,7 +49,7 @@ import kotlin.math.ln @Suppress("unused", "unused") class ExifInterface { - private var mData = ExifData(DEFAULT_BYTE_ORDER) + private var mData = ExifData() private val mGPSTimeStampCalendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")) @@ -58,7 +58,7 @@ class ExifInterface { * * @return a List of [ExifTag]s. */ - val allTags : List? + val allTags : List get() = mData.allTags val tagInfo : SparseIntArray by lazy { SparseIntArray().also { it.initTagInfo() } } @@ -69,16 +69,19 @@ class ExifInterface { * @return the thumbnail as a bitmap. */ val thumbnailBitmap : Bitmap? - get() = when { - mData.hasCompressedThumbnail() -> { - val thumb = mData.compressedThumbnail - BitmapFactory.decodeByteArray(thumb, 0, thumb !!.size) + get() { + val compressedThumbnail = mData.compressedThumbnail + if(compressedThumbnail != null) { + return BitmapFactory + .decodeByteArray(compressedThumbnail, 0, compressedThumbnail.size) + } + val stripList = mData.stripList + if(stripList != null) { + // TODO: decoding uncompressed thumbnail is not implemented. + return null } - // TODO: implement uncompressed - mData.hasUncompressedStrip() -> null - - else -> null + return null } /** @@ -270,18 +273,8 @@ class ExifInterface { * @see .readExif */ @Throws(IOException::class) - fun readExif(inFileName : String?, options : Int) { - requireNotNull(inFileName) { NULL_ARGUMENT_STRING } - var `is` : InputStream? = null - try { - `is` = BufferedInputStream(FileInputStream(inFileName)) - readExif(`is`, options) - } catch(e : IOException) { - closeSilently(`is`) - throw e - } - - `is`.close() + fun readExif(inFileName : String, options : Int) { + BufferedInputStream(FileInputStream(inFileName)).use { readExif(it, options) } } /** @@ -301,11 +294,7 @@ class ExifInterface { */ @Throws(IOException::class) fun readExif(inStream : InputStream, options : Int) { - try { - mData = ExifReader(this).read(inStream, options) - } catch(e : ExifInvalidFormatException) { - throw IOException("Invalid exif format : $e") - } + mData = ExifReader(this).read(inStream, options) } /** @@ -323,7 +312,7 @@ class ExifInterface { * Clears this ExifInterface object's existing exif tags. */ private fun clearExif() { - mData = ExifData(DEFAULT_BYTE_ORDER) + mData = ExifData() } /** @@ -333,8 +322,7 @@ class ExifInterface { * @param tags a Collection of ExifTags. * @see .setTag */ - private fun setTags(tags : Collection?) { - if(null == tags) return + private fun setTags(tags : Collection) { for(t in tags) { setTag(t) } @@ -399,19 +387,17 @@ class ExifInterface { // exif tags are not used here // 3. rename dst file into backup file - val input = FileInputStream(srcFilename) - val output = FileOutputStream(dstFilename) - - 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() - - closeQuietly(input) - closeQuietly(output) + 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) @@ -435,7 +421,7 @@ class ExifInterface { fun writeExif(input : Bitmap, dstFilename : String, quality : Int) { Log.i(TAG, "writeExif: $dstFilename") - // inpur is used *ONLY* to read the image uncompressed data + // input is used *ONLY* to read the image uncompressed data // exif tags are not used here val out = ByteArrayOutputStream() @@ -547,162 +533,54 @@ class ExifInterface { return t?.getValue() } - /** - * @see .getTagValue - */ - private fun getTagStringValue(tagId : Int, ifdId : Int) : String? { - val t = getTag(tagId, ifdId) ?: return null - return t.valueAsString - } + private fun getTagStringValue( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : String? = + getTag(tagId, ifdId)?.valueAsString - /** - * @see .getTagValue - */ - private fun getTagStringValue(tagId : Int) : String? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagStringValue(tagId, ifdId) - } + // array - /** - * @see .getTagValue - */ - fun getTagLongValue(tagId : Int) : Long? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagLongValue(tagId, ifdId) - } + private fun getTagLongValues( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : LongArray? = + getTag(tagId, ifdId)?.valueAsLongs - /** - * @see .getTagValue - */ - private fun getTagLongValue(tagId : Int, ifdId : Int) : Long? { - val l = getTagLongValues(tagId, ifdId) - return if(l == null || l.isEmpty()) { - null - } else l[0] - } + private fun getTagIntValues( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : IntArray? = + getTag(tagId, ifdId)?.valueAsInts - /** - * @see .getTagValue - */ - private fun getTagLongValues(tagId : Int, ifdId : Int) : LongArray? { - val t = getTag(tagId, ifdId) ?: return null - return t.valueAsLongs - } + private fun getTagByteValues( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : ByteArray? = + getTag(tagId, ifdId)?.valueAsBytes - /** - * @see .getTagValue - */ - fun getTagIntValue(tagId : Int) : Int? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagIntValue(tagId, ifdId) - } + private fun getTagRationalValues( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : Array? = + getTag(tagId, ifdId)?.valueAsRationals - /** - * @see .getTagValue - */ - private fun getTagIntValue(tagId : Int, ifdId : Int) : Int? { - val l = getTagIntValues(tagId, ifdId) - return if(l == null || l.isEmpty()) { - null - } else l[0] - } + // single value - /** - * @see .getTagValue - */ - private fun getTagIntValues(tagId : Int, ifdId : Int) : IntArray? { - val t = getTag(tagId, ifdId) ?: return null - return t.valueAsInts - } + fun getTagLongValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Long? = + getTagLongValues(tagId, ifdId)?.firstOrNull() - /** - * @see .getTagValue - */ - private fun getTagByteValue(tagId : Int) : Byte? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagByteValue(tagId, ifdId) - } + fun getTagIntValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Int? = + getTagIntValues(tagId, ifdId)?.firstOrNull() - /** - * @see .getTagValue - */ - private fun getTagByteValue(tagId : Int, ifdId : Int) : Byte? { - val l = getTagByteValues(tagId, ifdId) - return if(l == null || l.isEmpty()) { - null - } else l[0] - } + private fun getTagByteValue(tagId : Int, ifdId : Int = getDefinedTagDefaultIfd(tagId)) : Byte? = + getTagByteValues(tagId, ifdId)?.firstOrNull() - /** - * @see .getTagValue - */ - private fun getTagByteValues(tagId : Int, ifdId : Int) : ByteArray? { - val t = getTag(tagId, ifdId) ?: return null - return t.valueAsBytes - } - - /** - * @see .getTagValue - */ - private fun getTagRationalValue(tagId : Int) : Rational? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagRationalValue(tagId, ifdId) - } - - /** - * @see .getTagValue - */ - private fun getTagRationalValue(tagId : Int, ifdId : Int) : Rational? { - val l = getTagRationalValues(tagId, ifdId) - return if(l == null || l.isEmpty()) { - null - } else Rational(l[0]) - } - - /* - * Getter methods that are similar to getTagValue. Null is returned if the - * tag value cannot be cast into the return type. - */ - - /** - * @see .getTagValue - */ - private fun getTagRationalValues(tagId : Int, ifdId : Int) : Array? { - val t = getTag(tagId, ifdId) ?: return null - return t.valueAsRationals - } - - /** - * @see .getTagValue - */ - fun getTagLongValues(tagId : Int) : LongArray? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagLongValues(tagId, ifdId) - } - - /** - * @see .getTagValue - */ - fun getTagIntValues(tagId : Int) : IntArray? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagIntValues(tagId, ifdId) - } - - /** - * @see .getTagValue - */ - fun getTagByteValues(tagId : Int) : ByteArray? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagByteValues(tagId, ifdId) - } - - /** - * @see .getTagValue - */ - private fun getTagRationalValues(tagId : Int) : Array? { - val ifdId = getDefinedTagDefaultIfd(tagId) - return getTagRationalValues(tagId, ifdId) - } + private fun getTagRationalValue( + tagId : Int, + ifdId : Int = getDefinedTagDefaultIfd(tagId) + ) : Rational? = + getTagRationalValues(tagId, ifdId)?.firstOrNull() /** * Checks whether a tag has a defined number of elements. @@ -881,7 +759,7 @@ class ExifInterface { } private fun getTagDefinitionsForTagId(tagId : Short) : IntArray? { - val ifds = IfdId.list + val ifds = IfdData.list val defs = IntArray(ifds.size) var counter = 0 val infos = tagInfo @@ -942,12 +820,12 @@ class ExifInterface { tagInfo.delete(tagId) } -// /** -// * Resets tag definitions to the default ones. -// */ -// fun resetTagDefinitions() { -// mTagInfo = null -// } + // /** + // * Resets tag definitions to the default ones. + // */ + // fun resetTagDefinitions() { + // mTagInfo = null + // } /** * Check if thumbnail exists. @@ -1642,9 +1520,10 @@ class ExifInterface { */ // IFD 0 - private val TAG_IMAGE_WIDTH = defineTag(IfdId.TYPE_IFD_0, 0x0100.toShort()) - private val TAG_IMAGE_LENGTH = defineTag(IfdId.TYPE_IFD_0, 0x0101.toShort()) // Image height - private val TAG_BITS_PER_SAMPLE = defineTag(IfdId.TYPE_IFD_0, 0x0102.toShort()) + 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.

@@ -1655,24 +1534,24 @@ class ExifInterface { * * 6 = JPEG compression (thumbnails only) * * Other = reserved */ - private val TAG_COMPRESSION = defineTag(IfdId.TYPE_IFD_0, 0x0103.toShort()) - private val TAG_PHOTOMETRIC_INTERPRETATION = defineTag(IfdId.TYPE_IFD_0, 0x0106.toShort()) - private val TAG_IMAGE_DESCRIPTION = defineTag(IfdId.TYPE_IFD_0, 0x010E.toShort()) + 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

* 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(IfdId.TYPE_IFD_0, 0x010F.toShort()) + private val TAG_MAKE = defineTag(IfdData.TYPE_IFD_0, 0x010F.toShort()) /** * Value is ascii string

* 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(IfdId.TYPE_IFD_0, 0x0110.toShort()) - val TAG_STRIP_OFFSETS = defineTag(IfdId.TYPE_IFD_0, 0x0111.toShort()) + 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

@@ -1690,25 +1569,26 @@ class ExifInterface { * * '9' undefined * */ - val TAG_ORIENTATION = defineTag(IfdId.TYPE_IFD_0, 0x0112.toShort()) - private val TAG_SAMPLES_PER_PIXEL = defineTag(IfdId.TYPE_IFD_0, 0x0115.toShort()) - private val TAG_ROWS_PER_STRIP = defineTag(IfdId.TYPE_IFD_0, 0x0116.toShort()) - val TAG_STRIP_BYTE_COUNTS = defineTag(IfdId.TYPE_IFD_0, 0x0117.toShort()) + 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(IfdId.TYPE_IFD_INTEROPERABILITY, 0x0002.toShort()) + private val TAG_INTEROP_VERSION = + defineTag(IfdData.TYPE_IFD_INTEROPERABILITY, 0x0002.toShort()) /** * Value is unsigned double.

* 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(IfdId.TYPE_IFD_0, 0x011A.toShort()) + private val TAG_X_RESOLUTION = defineTag(IfdData.TYPE_IFD_0, 0x011A.toShort()) /** * @see .TAG_X_RESOLUTION */ - private val TAG_Y_RESOLUTION = defineTag(IfdId.TYPE_IFD_0, 0x011B.toShort()) - private val TAG_PLANAR_CONFIGURATION = defineTag(IfdId.TYPE_IFD_0, 0x011C.toShort()) + 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.

@@ -1721,21 +1601,21 @@ class ExifInterface { * * '5' micrometer * */ - private val TAG_RESOLUTION_UNIT = defineTag(IfdId.TYPE_IFD_0, 0x0128.toShort()) - private val TAG_TRANSFER_FUNCTION = defineTag(IfdId.TYPE_IFD_0, 0x012D.toShort()) + 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

* Shows firmware(internal software of digicam) version number. */ - private val TAG_SOFTWARE = defineTag(IfdId.TYPE_IFD_0, 0x0131.toShort()) + private val TAG_SOFTWARE = defineTag(IfdData.TYPE_IFD_0, 0x0131.toShort()) /** * Value is ascii string (20)

* 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(IfdId.TYPE_IFD_0, 0x0132.toShort()) + val TAG_DATE_TIME = defineTag(IfdData.TYPE_IFD_0, 0x0132.toShort()) /** * Vallue is ascii String

@@ -1743,31 +1623,31 @@ class ExifInterface { * 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(IfdId.TYPE_IFD_0, 0x013B.toShort()) - private val TAG_WHITE_POINT = defineTag(IfdId.TYPE_IFD_0, 0x013E.toShort()) - private val TAG_PRIMARY_CHROMATICITIES = defineTag(IfdId.TYPE_IFD_0, 0x013F.toShort()) - private val TAG_Y_CB_CR_COEFFICIENTS = defineTag(IfdId.TYPE_IFD_0, 0x0211.toShort()) - private val TAG_Y_CB_CR_SUB_SAMPLING = defineTag(IfdId.TYPE_IFD_0, 0x0212.toShort()) - private val TAG_Y_CB_CR_POSITIONING = defineTag(IfdId.TYPE_IFD_0, 0x0213.toShort()) - private val TAG_REFERENCE_BLACK_WHITE = defineTag(IfdId.TYPE_IFD_0, 0x0214.toShort()) + 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

* Shows copyright information */ - private val TAG_COPYRIGHT = defineTag(IfdId.TYPE_IFD_0, 0x8298.toShort()) - val TAG_EXIF_IFD = defineTag(IfdId.TYPE_IFD_0, 0x8769.toShort()) - val TAG_GPS_IFD = defineTag(IfdId.TYPE_IFD_0, 0x8825.toShort()) + 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(IfdId.TYPE_IFD_1, 0x0201.toShort()) - val TAG_JPEG_INTERCHANGE_FORMAT_LENGTH = defineTag(IfdId.TYPE_IFD_1, 0x0202.toShort()) + 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

* Exposure time (reciprocal of shutter speed). Unit is second */ - private val TAG_EXPOSURE_TIME = defineTag(IfdId.TYPE_IFD_EXIF, 0x829A.toShort()) + private val TAG_EXPOSURE_TIME = defineTag(IfdData.TYPE_IFD_EXIF, 0x829A.toShort()) /** * Value is unsigned double

@@ -1775,7 +1655,7 @@ class ExifInterface { * * @see .TAG_APERTURE_VALUE */ - val TAG_F_NUMBER = defineTag(IfdId.TYPE_IFD_EXIF, 0x829D.toShort()) + val TAG_F_NUMBER = defineTag(IfdData.TYPE_IFD_EXIF, 0x829D.toShort()) /** * Value is unsigned int.

@@ -1791,44 +1671,46 @@ class ExifInterface { * * '8' landscape mode. * */ - private val TAG_EXPOSURE_PROGRAM = defineTag(IfdId.TYPE_IFD_EXIF, 0x8822.toShort()) - private val TAG_SPECTRAL_SENSITIVITY = defineTag(IfdId.TYPE_IFD_EXIF, 0x8824.toShort()) + 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.

* CCD sensitivity equivalent to Ag-Hr film speedrate.

* 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(IfdId.TYPE_IFD_EXIF, 0x8827.toShort()) - private val TAG_OECF = defineTag(IfdId.TYPE_IFD_EXIF, 0x8828.toShort()) + 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).

* 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(IfdId.TYPE_IFD_EXIF, 0x9000.toShort()) + private val TAG_EXIF_VERSION = defineTag(IfdData.TYPE_IFD_EXIF, 0x9000.toShort()) /** * Value is ascii string (20)

* Date/Time of original image taken. This value should not be modified by user program. */ - val TAG_DATE_TIME_ORIGINAL = defineTag(IfdId.TYPE_IFD_EXIF, 0x9003.toShort()) + val TAG_DATE_TIME_ORIGINAL = defineTag(IfdData.TYPE_IFD_EXIF, 0x9003.toShort()) /** * Value is ascii string (20)

* Date/Time of image digitized. Usually, it contains the same value of DateTimeOriginal(0x9003). */ - val TAG_DATE_TIME_DIGITIZED = defineTag(IfdId.TYPE_IFD_EXIF, 0x9004.toShort()) - private val TAG_COMPONENTS_CONFIGURATION = defineTag(IfdId.TYPE_IFD_EXIF, 0x9101.toShort()) - private val TAG_COMPRESSED_BITS_PER_PIXEL = defineTag(IfdId.TYPE_IFD_EXIF, 0x9102.toShort()) + 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.

* 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(IfdId.TYPE_IFD_EXIF, 0x9201.toShort()) + private val TAG_SHUTTER_SPEED_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9201.toShort()) /** * Value is unsigned double

@@ -1843,19 +1725,19 @@ class ExifInterface { * * @see .TAG_F_NUMBER */ - val TAG_APERTURE_VALUE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9202.toShort()) + val TAG_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9202.toShort()) /** * Value is signed double

* Brightness of taken subject, unit is EV. */ - private val TAG_BRIGHTNESS_VALUE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9203.toShort()) + private val TAG_BRIGHTNESS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9203.toShort()) /** * Value is signed double.

* 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(IfdId.TYPE_IFD_EXIF, 0x9204.toShort()) + private val TAG_EXPOSURE_BIAS_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9204.toShort()) /** * Value is unsigned double.

@@ -1867,13 +1749,13 @@ class ExifInterface { * FNumber = Math.exp( MaxApertureValue * Math.log( 2 ) * 0.5 ) * */ - private val TAG_MAX_APERTURE_VALUE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9205.toShort()) + private val TAG_MAX_APERTURE_VALUE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9205.toShort()) /** * Value if signed double.

* Distance to focus point, unit is meter. If value < 0 then focus point is infinite */ - private val TAG_SUBJECT_DISTANCE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9206.toShort()) + private val TAG_SUBJECT_DISTANCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9206.toShort()) /** * Value is unsigned int.

@@ -1890,7 +1772,7 @@ class ExifInterface { * * 255 = other * */ - private val TAG_METERING_MODE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9207.toShort()) + private val TAG_METERING_MODE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9207.toShort()) /** * Value is unsigned int.

@@ -1920,7 +1802,7 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_LIGHT_SOURCE = defineTag(IfdId.TYPE_IFD_EXIF, 0x9208.toShort()) + private val TAG_LIGHT_SOURCE = defineTag(IfdData.TYPE_IFD_EXIF, 0x9208.toShort()) /** * Value is unsigned integer

@@ -1964,20 +1846,20 @@ class ExifInterface { * * @see [http://www.exif.org/Exif2-2.PDF](http://www.exif.org/Exif2-2.PDF) */ - private val TAG_FLASH = defineTag(IfdId.TYPE_IFD_EXIF, 0x9209.toShort()) + private val TAG_FLASH = defineTag(IfdData.TYPE_IFD_EXIF, 0x9209.toShort()) /** * Value is unsigned double

* Focal length of lens used to take image. Unit is millimeter. */ - private val TAG_FOCAL_LENGTH = defineTag(IfdId.TYPE_IFD_EXIF, 0x920A.toShort()) - private val TAG_SUBJECT_AREA = defineTag(IfdId.TYPE_IFD_EXIF, 0x9214.toShort()) - private val TAG_MAKER_NOTE = defineTag(IfdId.TYPE_IFD_EXIF, 0x927C.toShort()) - val TAG_USER_COMMENT = defineTag(IfdId.TYPE_IFD_EXIF, 0x9286.toShort()) - private val TAG_SUB_SEC_TIME = defineTag(IfdId.TYPE_IFD_EXIF, 0x9290.toShort()) - private val TAG_SUB_SEC_TIME_ORIGINAL = defineTag(IfdId.TYPE_IFD_EXIF, 0x9291.toShort()) - private val TAG_SUB_SEC_TIME_DIGITIZED = defineTag(IfdId.TYPE_IFD_EXIF, 0x9292.toShort()) - private val TAG_FLASHPIX_VERSION = defineTag(IfdId.TYPE_IFD_EXIF, 0xA000.toShort()) + 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.

@@ -1990,7 +1872,7 @@ class ExifInterface { * * 'other' = Reserved * */ - private val TAG_COLOR_SPACE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA001.toShort()) + private val TAG_COLOR_SPACE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA001.toShort()) /** * Value is unsigned int.

@@ -1998,16 +1880,17 @@ class ExifInterface { * 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(IfdId.TYPE_IFD_EXIF, 0xA002.toShort()) + 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(IfdId.TYPE_IFD_EXIF, 0xA003.toShort()) - private val TAG_RELATED_SOUND_FILE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA004.toShort()) - val TAG_INTEROPERABILITY_IFD = defineTag(IfdId.TYPE_IFD_EXIF, 0xA005.toShort()) - private val TAG_FLASH_ENERGY = defineTag(IfdId.TYPE_IFD_EXIF, 0xA20B.toShort()) - private val TAG_SPATIAL_FREQUENCY_RESPONSE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA20C.toShort()) + 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.

@@ -2016,12 +1899,14 @@ class ExifInterface { * * @see .TAG_FOCAL_PLANE_RESOLUTION_UNIT */ - private val TAG_FOCAL_PLANE_X_RESOLUTION = defineTag(IfdId.TYPE_IFD_EXIF, 0xA20E.toShort()) + 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(IfdId.TYPE_IFD_EXIF, 0xA20F.toShort()) + private val TAG_FOCAL_PLANE_Y_RESOLUTION = + defineTag(IfdData.TYPE_IFD_EXIF, 0xA20F.toShort()) /** * Value is unsigned int.

@@ -2042,9 +1927,10 @@ class ExifInterface { * CCDWidth = ( PixelXDimension * FocalPlaneResolutionUnit / FocalPlaneXResolution ) * */ - private val TAG_FOCAL_PLANE_RESOLUTION_UNIT = defineTag(IfdId.TYPE_IFD_EXIF, 0xA210.toShort()) - private val TAG_SUBJECT_LOCATION = defineTag(IfdId.TYPE_IFD_EXIF, 0xA214.toShort()) - private val TAG_EXPOSURE_INDEX = defineTag(IfdId.TYPE_IFD_EXIF, 0xA215.toShort()) + 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.

@@ -2060,11 +1946,11 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_SENSING_METHOD = defineTag(IfdId.TYPE_IFD_EXIF, 0xA217.toShort()) - private val TAG_FILE_SOURCE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA300.toShort()) - private val TAG_SCENE_TYPE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA301.toShort()) - private val TAG_CFA_PATTERN = defineTag(IfdId.TYPE_IFD_EXIF, 0xA302.toShort()) - private val TAG_CUSTOM_RENDERED = defineTag(IfdId.TYPE_IFD_EXIF, 0xA401.toShort()) + 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.

@@ -2077,15 +1963,15 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_EXPOSURE_MODE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA402.toShort()) - private val TAG_WHITE_BALANCE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA403.toShort()) + 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.

* 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(IfdId.TYPE_IFD_EXIF, 0xA404.toShort()) + private val TAG_DIGITAL_ZOOM_RATIO = defineTag(IfdData.TYPE_IFD_EXIF, 0xA404.toShort()) /** * Value is unsigned int.

@@ -2098,7 +1984,8 @@ class ExifInterface { * FocalLengthIn35mmFilm = ( FocalLength / CCDWidth * 36 + 0.5 ); * */ - private val TAG_FOCAL_LENGTH_IN_35_MM_FILE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA405.toShort()) + private val TAG_FOCAL_LENGTH_IN_35_MM_FILE = + defineTag(IfdData.TYPE_IFD_EXIF, 0xA405.toShort()) /** * Value is int.

@@ -2112,7 +1999,7 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_SCENE_CAPTURE_TYPE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA406.toShort()) + private val TAG_SCENE_CAPTURE_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA406.toShort()) /** * Value is int.

@@ -2126,7 +2013,7 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_GAIN_CONTROL = defineTag(IfdId.TYPE_IFD_EXIF, 0xA407.toShort()) + private val TAG_GAIN_CONTROL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA407.toShort()) /** * Value is int.

@@ -2138,7 +2025,7 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_CONTRAST = defineTag(IfdId.TYPE_IFD_EXIF, 0xA408.toShort()) + private val TAG_CONTRAST = defineTag(IfdData.TYPE_IFD_EXIF, 0xA408.toShort()) /** * Value is int.

@@ -2150,7 +2037,7 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_SATURATION = defineTag(IfdId.TYPE_IFD_EXIF, 0xA409.toShort()) + private val TAG_SATURATION = defineTag(IfdData.TYPE_IFD_EXIF, 0xA409.toShort()) /** * Value is int.

@@ -2162,8 +2049,9 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_SHARPNESS = defineTag(IfdId.TYPE_IFD_EXIF, 0xA40A.toShort()) - private val TAG_DEVICE_SETTING_DESCRIPTION = defineTag(IfdId.TYPE_IFD_EXIF, 0xA40B.toShort()) + 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.

@@ -2176,12 +2064,12 @@ class ExifInterface { * * Other = reserved * */ - private val TAG_SUBJECT_DISTANCE_RANGE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA40C.toShort()) + private val TAG_SUBJECT_DISTANCE_RANGE = defineTag(IfdData.TYPE_IFD_EXIF, 0xA40C.toShort()) /** * [ExifTag.TYPE_ASCII] */ - private val TAG_IMAGE_UNIQUE_ID = defineTag(IfdId.TYPE_IFD_EXIF, 0xA420.toShort()) + private val TAG_IMAGE_UNIQUE_ID = defineTag(IfdData.TYPE_IFD_EXIF, 0xA420.toShort()) /** * Lens Specifications. The value it's a 4 rational containing: @@ -2198,7 +2086,7 @@ class ExifInterface { * @see it.sephiroth.android.library.exif2.ExifUtil.processLensSpecifications * @since EXIF 2.3 */ - val TAG_LENS_SPECS = defineTag(IfdId.TYPE_IFD_EXIF, 0xA432.toShort()) + val TAG_LENS_SPECS = defineTag(IfdData.TYPE_IFD_EXIF, 0xA432.toShort()) /** * Lens maker @@ -2206,14 +2094,14 @@ class ExifInterface { * * @since EXIF 2.3 */ - private val TAG_LENS_MAKE = defineTag(IfdId.TYPE_IFD_EXIF, 0xA433.toShort()) + 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(IfdId.TYPE_IFD_EXIF, 0xA434.toShort()) + val TAG_LENS_MODEL = defineTag(IfdData.TYPE_IFD_EXIF, 0xA434.toShort()) /** * The SensitivityType tag indicates which one of the parameters of ISO12232 is the @@ -2241,16 +2129,16 @@ class ExifInterface { * * @since EXIF 2.3 */ - private val TAG_SENSITIVITY_TYPE = defineTag(IfdId.TYPE_IFD_EXIF, 0x8830.toShort()) + private val TAG_SENSITIVITY_TYPE = defineTag(IfdData.TYPE_IFD_EXIF, 0x8830.toShort()) // IFD GPS tags - private val TAG_GPS_VERSION_ID = defineTag(IfdId.TYPE_IFD_GPS, 0.toShort()) + private val TAG_GPS_VERSION_ID = defineTag(IfdData.TYPE_IFD_GPS, 0.toShort()) /** * Value is string(1)

* 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(IfdId.TYPE_IFD_GPS, 1.toShort()) + val TAG_GPS_LATITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 1.toShort()) /** * Value is string.

@@ -2259,13 +2147,13 @@ class ExifInterface { * 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(IfdId.TYPE_IFD_GPS, 2.toShort()) + val TAG_GPS_LATITUDE = defineTag(IfdData.TYPE_IFD_GPS, 2.toShort()) /** * Value is string(1)

* 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(IfdId.TYPE_IFD_GPS, 3.toShort()) + val TAG_GPS_LONGITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 3.toShort()) /** * Value is string.

@@ -2274,7 +2162,7 @@ class ExifInterface { * 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(IfdId.TYPE_IFD_GPS, 4.toShort()) + val TAG_GPS_LONGITUDE = defineTag(IfdData.TYPE_IFD_GPS, 4.toShort()) /** * Value is byte

@@ -2282,49 +2170,50 @@ class ExifInterface { * 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(IfdId.TYPE_IFD_GPS, 5.toShort()) + val TAG_GPS_ALTITUDE_REF = defineTag(IfdData.TYPE_IFD_GPS, 5.toShort()) /** * Value is string.

* 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(IfdId.TYPE_IFD_GPS, 6.toShort()) - val TAG_GPS_TIME_STAMP = defineTag(IfdId.TYPE_IFD_GPS, 7.toShort()) - private val TAG_GPS_SATTELLITES = defineTag(IfdId.TYPE_IFD_GPS, 8.toShort()) - private val TAG_GPS_STATUS = defineTag(IfdId.TYPE_IFD_GPS, 9.toShort()) - private val TAG_GPS_MEASURE_MODE = defineTag(IfdId.TYPE_IFD_GPS, 10.toShort()) - private val TAG_GPS_DOP = defineTag(IfdId.TYPE_IFD_GPS, 11.toShort()) + 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).

* 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(IfdId.TYPE_IFD_GPS, 12.toShort()) + private val TAG_GPS_SPEED_REF = defineTag(IfdData.TYPE_IFD_GPS, 12.toShort()) /** * Value is string.

* Indicates the speed of GPS receiver movement */ - private val TAG_GPS_SPEED = defineTag(IfdId.TYPE_IFD_GPS, 13.toShort()) - private val TAG_GPS_TRACK_REF = defineTag(IfdId.TYPE_IFD_GPS, 14.toShort()) - private val TAG_GPS_TRACK = defineTag(IfdId.TYPE_IFD_GPS, 15.toShort()) - private val TAG_GPS_IMG_DIRECTION_REF = defineTag(IfdId.TYPE_IFD_GPS, 16.toShort()) - private val TAG_GPS_IMG_DIRECTION = defineTag(IfdId.TYPE_IFD_GPS, 17.toShort()) - private val TAG_GPS_MAP_DATUM = defineTag(IfdId.TYPE_IFD_GPS, 18.toShort()) - private val TAG_GPS_DEST_LATITUDE_REF = defineTag(IfdId.TYPE_IFD_GPS, 19.toShort()) - private val TAG_GPS_DEST_LATITUDE = defineTag(IfdId.TYPE_IFD_GPS, 20.toShort()) - val TAG_GPS_DEST_LONGITUDE_REF = defineTag(IfdId.TYPE_IFD_GPS, 21.toShort()) - val TAG_GPS_DEST_LONGITUDE = defineTag(IfdId.TYPE_IFD_GPS, 22.toShort()) - private val TAG_GPS_DEST_BEARING_REF = defineTag(IfdId.TYPE_IFD_GPS, 23.toShort()) - private val TAG_GPS_DEST_BEARING = defineTag(IfdId.TYPE_IFD_GPS, 24.toShort()) - private val TAG_GPS_DEST_DISTANCE_REF = defineTag(IfdId.TYPE_IFD_GPS, 25.toShort()) - private val TAG_GPS_DEST_DISTANCE = defineTag(IfdId.TYPE_IFD_GPS, 26.toShort()) - private val TAG_GPS_PROCESSING_METHOD = defineTag(IfdId.TYPE_IFD_GPS, 27.toShort()) - private val TAG_GPS_AREA_INFORMATION = defineTag(IfdId.TYPE_IFD_GPS, 28.toShort()) - val TAG_GPS_DATE_STAMP = defineTag(IfdId.TYPE_IFD_GPS, 29.toShort()) - private val TAG_GPS_DIFFERENTIAL = defineTag(IfdId.TYPE_IFD_GPS, 30.toShort()) + 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(IfdId.TYPE_IFD_INTEROPERABILITY, 1.toShort()) + 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" @@ -2438,9 +2327,9 @@ class ExifInterface { fun getAllowedIfdsFromInfo(info : Int) : IntArray? { val ifdFlags = getAllowedIfdFlagsFromInfo(info) - val ifds = IfdId.list + val ifds = IfdData.list val l = ArrayList() - for(i in 0 until IfdId.TYPE_IFD_COUNT) { + for(i in 0 until IfdData.TYPE_IFD_COUNT) { val flag = ifdFlags shr i and 1 if(flag == 1) { l.add(ifds[i]) @@ -2457,27 +2346,6 @@ class ExifInterface { return ret } - fun closeSilently(c : Closeable?) { - if(c != null) { - try { - c.close() - } catch(e : Throwable) { - // ignored - } - - } - } - - fun closeQuietly(input : InputStream) { - @Suppress("DEPRECATION") - IOUtils.closeQuietly(input) - } - - fun closeQuietly(output : OutputStream) { - @Suppress("DEPRECATION") - IOUtils.closeQuietly(output) - } - @Throws(IOException::class) private fun writeExif_internal( input : InputStream, @@ -2495,26 +2363,24 @@ class ExifInterface { output.write(0xFF) output.write(JpegHeader.TAG_SOI) - val sections = src_exif.mData.sections !! + val sections = src_exif.mData.sections // 6. write all the sections from the srcFilename - if(sections[0].type != JpegHeader.TAG_M_JFIF) { + 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) - eo.exifData = exifData + val eo = ExifOutputStream(src_exif, exifData) eo.writeExifData(output) // 6.2 write all the sections except for the SOS ( start of scan ) - for(a in 0 until sections.size - 1) { - val current = sections[a] + sections.forEach { // Log.v( TAG, "writing section.. " + String.format( "0x%2X", current.type ) ); output.write(0xFF) - output.write(current.type) - output.write(current.data !!) + output.write(it.type) + output.write(it.data) } // 6.3 write the last SOS marker @@ -2522,7 +2388,7 @@ class ExifInterface { // Log.v( TAG, "writing last section.. " + String.format( "0x%2X", current.type ) ); output.write(0xFF) output.write(current.type) - output.write(current.data !!) + output.write(current.data) // return the position where the input stream should be copied return src_exif.mData.mUncompressedDataPosition @@ -2548,8 +2414,8 @@ class ExifInterface { return 0 } var flags = 0 - val ifds = IfdId.list - for(i in 0 until IfdId.TYPE_IFD_COUNT) { + 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) @@ -2623,7 +2489,7 @@ class ExifInterface { } fun isIfdAllowed(info : Int, ifd : Int) : Boolean { - val ifds = IfdId.list + val ifds = IfdData.list val ifdFlags = getAllowedIfdFlagsFromInfo(info) for(i in ifds.indices) { if(ifd == ifds[i] && ifdFlags shr i and 1 == 1) { @@ -2676,7 +2542,7 @@ class ExifInterface { */ // IFD0 tags - f = getFlagsFromAllowedIfds(intArrayOf(IfdId.TYPE_IFD_0, IfdId.TYPE_IFD_1)) shl 24 + 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) @@ -2710,12 +2576,12 @@ class ExifInterface { put(TAG_GPS_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1) // IFD1 tags - f = getFlagsFromAllowedIfds(intArrayOf(IfdId.TYPE_IFD_1)) shl 24 + 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(IfdId.TYPE_IFD_EXIF)) shl 24 + 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) @@ -2779,7 +2645,7 @@ class ExifInterface { put(TAG_INTEROPERABILITY_IFD, f or (ExifTag.TYPE_UNSIGNED_LONG shl 16) or 1) // GPS tag - f = getFlagsFromAllowedIfds(intArrayOf(IfdId.TYPE_IFD_GPS)) shl 24 + 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) @@ -2811,7 +2677,7 @@ class ExifInterface { put(TAG_GPS_DIFFERENTIAL, f or (ExifTag.TYPE_UNSIGNED_SHORT shl 16) or 11) // Interoperability tag - f = getFlagsFromAllowedIfds(intArrayOf(IfdId.TYPE_IFD_INTEROPERABILITY)) shl 24 + 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) } diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt index 2f2e0ab5..b20299c1 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifOutputStream.kt @@ -17,25 +17,23 @@ package it.sephiroth.android.library.exif2 import android.util.Log - +import it.sephiroth.android.library.exif2.utils.OrderedDataOutputStream import java.io.BufferedOutputStream import java.io.IOException import java.io.OutputStream import java.nio.ByteBuffer import java.nio.ByteOrder -import java.util.ArrayList +import java.util.* @Suppress("unused") -internal class ExifOutputStream(private val mInterface : ExifInterface) { - /** - * Gets the Exif header to be written into the JPEF file. - */ - /** - * Sets the ExifData to be written into the JPEG file. Should be called - * before writing image data. - */ - var exifData : ExifData? = null - +internal class ExifOutputStream( + + private val mInterface : ExifInterface, + + // the Exif header to be written into the JPEG file. + private val exifData : ExifData +) { + private val mBuffer = ByteBuffer.allocate(4) private fun requestByteToBuffer( @@ -49,13 +47,9 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { @Throws(IOException::class) fun writeExifData(out : OutputStream) { - if(exifData == null) { - return - } - Log.v(TAG, "Writing exif data...") - val nullTags = stripNullValueTags(exifData !!) + val nullTags = stripNullValueTags(exifData) createRequiredIfdAndTag() val exifSize = calculateAllOffset() // Log.i(TAG, "exifSize: " + (exifSize + 8)); @@ -64,7 +58,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { } val outputStream = BufferedOutputStream(out, STREAMBUFFER_SIZE) - val dataOutputStream = OrderedDataOutputStream(outputStream) + val dataOutputStream = + OrderedDataOutputStream(outputStream) dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN) @@ -73,12 +68,12 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { dataOutputStream.writeShort((exifSize + 8).toShort()) dataOutputStream.writeInt(EXIF_HEADER) dataOutputStream.writeShort(0x0000.toShort()) - if(exifData !!.byteOrder == ByteOrder.BIG_ENDIAN) { + if(exifData.byteOrder == ByteOrder.BIG_ENDIAN) { dataOutputStream.writeShort(TIFF_BIG_ENDIAN) } else { dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN) } - dataOutputStream.setByteOrder(exifData !!.byteOrder) + dataOutputStream.setByteOrder(exifData.byteOrder) dataOutputStream.writeShort(TIFF_HEADER) dataOutputStream.writeInt(8) writeAllTags(dataOutputStream) @@ -86,57 +81,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { writeThumbnail(dataOutputStream) for(t in nullTags) { - exifData !!.addTag(t) + exifData.addTag(t) } dataOutputStream.flush() } - private fun stripNullValueTags(data : ExifData) : ArrayList { - val nullTags = ArrayList() - for(t in data.allTags !!) { - if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) { - data.removeTag(t.tagId, t.ifd) - nullTags.add(t) + // strip tags that has null value + // return list of removed tags + private fun stripNullValueTags(data : ExifData) = + ArrayList() + .apply { + for(t in data.allTags) { + if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) { + data.removeTag(t.tagId, t.ifd) + add(t) + } + } } - } - return nullTags - } @Throws(IOException::class) private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) { - if(exifData !!.hasCompressedThumbnail()) { + val compressedThumbnail = exifData.compressedThumbnail + if(compressedThumbnail != null) { Log.d(TAG, "writing thumbnail..") - dataOutputStream.write(exifData !!.compressedThumbnail !!) - } else if(exifData !!.hasUncompressedStrip()) { - Log.d(TAG, "writing uncompressed strip..") - for(i in 0 until exifData !!.stripCount) { - dataOutputStream.write(exifData !!.getStrip(i) !!) + dataOutputStream.write(compressedThumbnail) + } else { + val stripList = exifData.stripList + if(stripList != null) { + Log.d(TAG, "writing uncompressed strip..") + stripList.forEach { + dataOutputStream.write(it) + } } } } @Throws(IOException::class) private fun writeAllTags(dataOutputStream : OrderedDataOutputStream) { - writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_0) !!, dataOutputStream) - writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_EXIF) !!, dataOutputStream) - val interoperabilityIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY) - if(interoperabilityIfd != null) { - writeIfd(interoperabilityIfd, dataOutputStream) - } - val gpsIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_GPS) - if(gpsIfd != null) { - writeIfd(gpsIfd, dataOutputStream) - } - val ifd1 = exifData !!.getIfdData(IfdId.TYPE_IFD_1) - if(ifd1 != null) { - writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_1) !!, dataOutputStream) - } + writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_0), dataOutputStream) + writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_EXIF), dataOutputStream) + writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY), dataOutputStream) + writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_GPS), dataOutputStream) + writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_1), dataOutputStream) } @Throws(IOException::class) - private fun writeIfd(ifd : IfdData, dataOutputStream : OrderedDataOutputStream) { - val tags = ifd.allTags + private fun writeIfd(ifd : IfdData?, dataOutputStream : OrderedDataOutputStream) { + ifd ?: return + val tags = ifd.allTagsCollection dataOutputStream.writeShort(tags.size.toShort()) for(tag in tags) { dataOutputStream.writeShort(tag.tagId) @@ -166,8 +159,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { private fun calculateOffsetOfIfd(ifd : IfdData, offsetArg : Int) : Int { var offset = offsetArg offset += 2 + ifd.tagCount * TAG_SIZE + 4 - val tags = ifd.allTags - for(tag in tags) { + + for(tag in ifd.allTagsCollection) { if(tag.dataSize > 4) { tag.offset = offset offset += tag.dataSize @@ -178,49 +171,53 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { @Throws(IOException::class) private fun createRequiredIfdAndTag() { + // IFD0 is required for all file - var ifd0 = exifData !!.getIfdData(IfdId.TYPE_IFD_0) + var ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0) if(ifd0 == null) { - ifd0 = IfdData(IfdId.TYPE_IFD_0) - exifData !!.addIfdData(ifd0) + ifd0 = IfdData(IfdData.TYPE_IFD_0) + exifData.addIfdData(ifd0) } + val exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD) ?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_EXIF_IFD) ifd0.setTag(exifOffsetTag) // Exif IFD is required for all files. - var exifIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_EXIF) + var exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF) if(exifIfd == null) { - exifIfd = IfdData(IfdId.TYPE_IFD_EXIF) - exifData !!.addIfdData(exifIfd) + exifIfd = IfdData(IfdData.TYPE_IFD_EXIF) + exifData.addIfdData(exifIfd) } // GPS IFD - val gpsIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_GPS) + val gpsIfd = exifData.getIfdData(IfdData.TYPE_IFD_GPS) if(gpsIfd != null) { val gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD) - ?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_GPS_IFD) + ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_GPS_IFD}") ifd0.setTag(gpsOffsetTag) } // Interoperability IFD - val interIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY) + val interIfd = exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY) if(interIfd != null) { val interOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD) - ?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_INTEROPERABILITY_IFD) + ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_INTEROPERABILITY_IFD}") exifIfd.setTag(interOffsetTag) } - var ifd1 = exifData !!.getIfdData(IfdId.TYPE_IFD_1) + var ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1) // thumbnail + val compressedThumbnail = exifData.compressedThumbnail + val stripList = exifData.stripList when { - exifData !!.hasCompressedThumbnail() -> { - + + compressedThumbnail != null -> { if(ifd1 == null) { - ifd1 = IfdData(IfdId.TYPE_IFD_1) - exifData !!.addIfdData(ifd1) + ifd1 = IfdData(IfdData.TYPE_IFD_1) + exifData.addIfdData(ifd1) } val offsetTag = @@ -232,34 +229,35 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH) ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH}") - lengthTag.setValue(exifData !!.compressedThumbnail !!.size) + lengthTag.setValue(compressedThumbnail.size) ifd1.setTag(lengthTag) // Get rid of tags for uncompressed if they exist. ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)) ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS)) } - exifData !!.hasUncompressedStrip() -> { + + stripList != null -> { if(ifd1 == null) { - ifd1 = IfdData(IfdId.TYPE_IFD_1) - exifData !!.addIfdData(ifd1) + ifd1 = IfdData(IfdData.TYPE_IFD_1) + exifData.addIfdData(ifd1) } - val stripCount = exifData !!.stripCount val offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS) ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_OFFSETS}") - val lengthTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS) - ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}") - val lengths = LongArray(stripCount) - for(i in 0 until exifData !!.stripCount) { - lengths[i] = exifData !!.getStrip(i) !!.size.toLong() - } - lengthTag.setValue(lengths) + val lengthTag = + mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS) + ?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}") + + val bytesList = LongArray(stripList.size) + stripList.forEachIndexed { index, bytes -> bytesList[index] = bytes.size.toLong() } + lengthTag.setValue(bytesList) ifd1.setTag(offsetTag) ifd1.setTag(lengthTag) // Get rid of tags for compressed if they exist. ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)) } + ifd1 != null -> { // Get rid of offset and length tags if there is no thumbnail. ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)) @@ -272,50 +270,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) { private fun calculateAllOffset() : Int { var offset = TIFF_HEADER_SIZE.toInt() - val exifData = this.exifData !! - val ifd0 = exifData.getIfdData(IfdId.TYPE_IFD_0) - offset = calculateOffsetOfIfd(ifd0 !!, offset) - ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)) - ?.setValue(offset) - val exifIfd = exifData.getIfdData(IfdId.TYPE_IFD_EXIF) - offset = calculateOffsetOfIfd(exifIfd !!, offset) - - val interIfd = exifData.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY) - if(interIfd != null) { - exifIfd.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD)) - ?.setValue(offset) - offset = calculateOffsetOfIfd(interIfd, offset) + val ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)?.also { + offset = calculateOffsetOfIfd(it, offset) } - val gpsIfd = exifData.getIfdData(IfdId.TYPE_IFD_GPS) - if(gpsIfd != null) { - ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)) + val exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)?.also { it -> + ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD)) ?.setValue(offset) - offset = calculateOffsetOfIfd(gpsIfd, offset) + offset = calculateOffsetOfIfd(it, offset) } - val ifd1 = exifData.getIfdData(IfdId.TYPE_IFD_1) - if(ifd1 != null) { - ifd0.offsetToNextIfd = offset - offset = calculateOffsetOfIfd(ifd1, offset) + exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)?.also { it -> + exifIfd?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD)) + ?.setValue(offset) + offset = calculateOffsetOfIfd(it, offset) } - // thumbnail - if(exifData .hasCompressedThumbnail()) { - ifd1 !!.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) + exifData.getIfdData(IfdData.TYPE_IFD_GPS)?.also { + ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD)) ?.setValue(offset) - offset += exifData .compressedThumbnail !!.size - } else if(exifData .hasUncompressedStrip()) { - val stripCount = exifData .stripCount - val offsets = LongArray(stripCount) - for(i in 0 until exifData .stripCount) { - offsets[i] = offset.toLong() - offset += exifData .getStrip(i) !!.size + offset = calculateOffsetOfIfd(it, offset) + } + + val ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)?.also { + ifd0?.offsetToNextIfd = offset + offset = calculateOffsetOfIfd(it, offset) + } + + val compressedThumbnail = exifData.compressedThumbnail + if(compressedThumbnail != null) { + ifd1 + ?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT)) + ?.setValue(offset) + offset += compressedThumbnail.size + } else { + // uncompressed thumbnail + val stripList = exifData.stripList + if(stripList != null) { + val offsets = LongArray(stripList.size) + stripList.forEachIndexed { index, bytes -> + offsets[index] = offset.toLong() + offset += bytes.size + } + ifd1 + ?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)) + ?.setValue(offsets) } - ifd1 !!.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS)) - ?.setValue(offsets) } + return offset } diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt index 8b835d01..6f4d99c1 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifParser.kt @@ -17,6 +17,7 @@ package it.sephiroth.android.library.exif2 import android.util.Log +import it.sephiroth.android.library.exif2.utils.CountedDataInputStream import java.io.ByteArrayInputStream import java.io.IOException import java.io.InputStream @@ -40,15 +41,15 @@ private constructor( /** * the ID of current IFD. * - * @see IfdId.TYPE_IFD_0 - * @see IfdId.TYPE_IFD_1 - * @see IfdId.TYPE_IFD_GPS - * @see IfdId.TYPE_IFD_INTEROPERABILITY - * @see IfdId.TYPE_IFD_EXIF + * @see IfdData.TYPE_IFD_0 + * @see IfdData.TYPE_IFD_1 + * @see IfdData.TYPE_IFD_GPS + * @see IfdData.TYPE_IFD_INTEROPERABILITY + * @see IfdData.TYPE_IFD_EXIF */ var currentIfd : Int = 0 private set - + /** * If [.next] return [.EVENT_NEW_TAG] or * [.EVENT_VALUE_OF_REGISTERED_TAG], call this function to get the @@ -154,10 +155,10 @@ private constructor( throw ExifInvalidFormatException("Invalid offset $offset") } mIfd0Position = offset.toInt() - currentIfd = IfdId.TYPE_IFD_0 + currentIfd = IfdData.TYPE_IFD_0 - if(isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) { - registerIfd(IfdId.TYPE_IFD_0, offset) + if(isIfdRequested(IfdData.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) { + registerIfd(IfdData.TYPE_IFD_0, offset) if(offset != DEFAULT_IFD0_OFFSET.toLong()) { val ba = ByteArray(offset.toInt() - DEFAULT_IFD0_OFFSET) mDataAboveIfd0 = ba @@ -182,15 +183,16 @@ private constructor( @Throws(IOException::class, ExifInvalidFormatException::class) private fun seekTiffData(inputStream : InputStream) : CountedDataInputStream { - val dataStream = CountedDataInputStream(inputStream) + val dataStream = + CountedDataInputStream(inputStream) var tiffStream : CountedDataInputStream? = null var a = dataStream.readUnsignedByte() val b = dataStream.readUnsignedByte() - + if(a == 137 && b == 80) error("maybe PNG image") - + if(a != 0xFF || b != JpegHeader.TAG_SOI) error("invalid jpeg header") while(true) { @@ -213,9 +215,6 @@ private constructor( Log.w(TAG, "Extraneous ${a - 1} padding bytes before section $marker") } - val section = Section() - section.type = marker - // Read the length of the section. val lh = dataStream.readByte().toInt() val ll = dataStream.readByte().toInt() @@ -225,8 +224,6 @@ private constructor( throw ExifInvalidFormatException("Invalid marker") } - section.size = itemlen - data = ByteArray(itemlen) data[0] = lh.toByte() data[1] = ll.toByte() @@ -240,7 +237,7 @@ private constructor( throw ExifInvalidFormatException("Premature end of file? Expecting " + (itemlen - 2) + ", received " + got) } - section.data = data + val section = Section(type = marker, size = itemlen, data = data) var ignore = false @@ -286,7 +283,9 @@ private constructor( // header = Exif, headerTail=\0\0 if(header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) { tiffStream = - CountedDataInputStream(ByteArrayInputStream(data, 8, itemlen - 8)) + CountedDataInputStream( + ByteArrayInputStream(data, 8, itemlen - 8) + ) tiffStream.end = itemlen - 6 ignore = false } else { @@ -428,11 +427,11 @@ private constructor( private fun isIfdRequested(ifdType : Int) : Boolean { when(ifdType) { - IfdId.TYPE_IFD_0 -> return mOptions and ExifInterface.Options.OPTION_IFD_0 != 0 - IfdId.TYPE_IFD_1 -> return mOptions and ExifInterface.Options.OPTION_IFD_1 != 0 - IfdId.TYPE_IFD_EXIF -> return mOptions and ExifInterface.Options.OPTION_IFD_EXIF != 0 - IfdId.TYPE_IFD_GPS -> return mOptions and ExifInterface.Options.OPTION_IFD_GPS != 0 - IfdId.TYPE_IFD_INTEROPERABILITY -> return mOptions and ExifInterface.Options.OPTION_IFD_INTEROPERABILITY != 0 + IfdData.TYPE_IFD_0 -> return mOptions and ExifInterface.Options.OPTION_IFD_0 != 0 + IfdData.TYPE_IFD_1 -> return mOptions and ExifInterface.Options.OPTION_IFD_1 != 0 + IfdData.TYPE_IFD_EXIF -> return mOptions and ExifInterface.Options.OPTION_IFD_EXIF != 0 + IfdData.TYPE_IFD_GPS -> return mOptions and ExifInterface.Options.OPTION_IFD_GPS != 0 + IfdData.TYPE_IFD_INTEROPERABILITY -> return mOptions and ExifInterface.Options.OPTION_IFD_INTEROPERABILITY != 0 } return false } @@ -440,17 +439,17 @@ private constructor( private fun needToParseOffsetsInCurrentIfd() : Boolean { return when(currentIfd) { - IfdId.TYPE_IFD_0 -> - isIfdRequested(IfdId.TYPE_IFD_EXIF) || - isIfdRequested(IfdId.TYPE_IFD_GPS) || - isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) || - isIfdRequested(IfdId.TYPE_IFD_1) + IfdData.TYPE_IFD_0 -> + isIfdRequested(IfdData.TYPE_IFD_EXIF) || + isIfdRequested(IfdData.TYPE_IFD_GPS) || + isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY) || + isIfdRequested(IfdData.TYPE_IFD_1) - IfdId.TYPE_IFD_1 -> isThumbnailRequested + IfdData.TYPE_IFD_1 -> isThumbnailRequested - IfdId.TYPE_IFD_EXIF -> + IfdData.TYPE_IFD_EXIF -> // The offset to interoperability IFD is located in Exif IFD - isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) + isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY) else -> false } @@ -508,11 +507,11 @@ private constructor( return EVENT_NEW_TAG } else if(offset == endOfTags) { // There is a link to ifd1 at the end of ifd0 - if(currentIfd == IfdId.TYPE_IFD_0) { + if(currentIfd == IfdData.TYPE_IFD_0) { val ifdOffset = readUnsignedLong() - if(isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested) { + if(isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested) { if(ifdOffset != 0L) { - registerIfd(IfdId.TYPE_IFD_1, ifdOffset) + registerIfd(IfdData.TYPE_IFD_1, ifdOffset) } } } else { @@ -610,9 +609,9 @@ private constructor( } val ifdOffset = readUnsignedLong() // For ifd0, there is a link to ifd1 in the end of all tags - if(currentIfd == IfdId.TYPE_IFD_0 && (isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested)) { + if(currentIfd == IfdData.TYPE_IFD_0 && (isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested)) { if(ifdOffset > 0) { - registerIfd(IfdId.TYPE_IFD_1, ifdOffset) + registerIfd(IfdData.TYPE_IFD_1, ifdOffset) } } } @@ -720,19 +719,19 @@ private constructor( val tid = tag.tagId val ifd = tag.ifd if(tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) { - if(isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) { - registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0)) + if(isIfdRequested(IfdData.TYPE_IFD_EXIF) || isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) { + registerIfd(IfdData.TYPE_IFD_EXIF, tag.getValueAt(0)) } } else if(tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) { - if(isIfdRequested(IfdId.TYPE_IFD_GPS)) { - registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0)) + if(isIfdRequested(IfdData.TYPE_IFD_GPS)) { + registerIfd(IfdData.TYPE_IFD_GPS, tag.getValueAt(0)) } } else if(tid == TAG_INTEROPERABILITY_IFD && checkAllowed( ifd, ExifInterface.TAG_INTEROPERABILITY_IFD )) { - if(isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) { - registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0)) + if(isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) { + registerIfd(IfdData.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0)) } } else if(tid == TAG_JPEG_INTERCHANGE_FORMAT && checkAllowed( ifd, @@ -750,7 +749,7 @@ private constructor( } } else if(tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) { if(isThumbnailRequested) { - if(tag.hasValue()) { + if(tag.hasValue) { for(i in 0 until tag.componentCount) { if(tag.dataType == ExifTag.TYPE_UNSIGNED_SHORT) { registerUncompressedStrip(i, tag.getValueAt(i)) @@ -765,16 +764,16 @@ private constructor( } else if(tid == TAG_STRIP_BYTE_COUNTS && checkAllowed( ifd, ExifInterface.TAG_STRIP_BYTE_COUNTS - ) && isThumbnailRequested && tag.hasValue()) { + ) && isThumbnailRequested && tag.hasValue) { mStripSizeTag = tag } } - fun isDefinedTag(ifdId : Int, tagId : Int) : Boolean { + fun isDefinedTag(ifdId : Int, tagId : Short) : Boolean { return mInterface.tagInfo.get( ExifInterface.defineTag( ifdId, - tagId.toShort() + tagId ) ) != ExifInterface.DEFINITION_NULL } @@ -946,12 +945,8 @@ private constructor( internal var isRequested : Boolean ) - class Section { - internal var size : Int = 0 - internal var type : Int = 0 - internal var data : ByteArray? = null - } - + class Section(var size : Int, var type : Int, var data : ByteArray) + companion object { private const val TAG = "ExifParser" diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt index 94c59588..078dcfe0 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifReader.kt @@ -37,12 +37,13 @@ internal class ExifReader(private val mInterface : ExifInterface) { @Throws(ExifInvalidFormatException::class, IOException::class) fun read(inputStream : InputStream, options : Int) : ExifData { val parser = ExifParser.parse(inputStream, options, mInterface) - val exifData = ExifData(parser.byteOrder ) - exifData.sections = parser.sections - exifData.mUncompressedDataPosition = parser.uncompressedDataPosition - - exifData.qualityGuess = parser.qualityGuess - exifData.jpegProcess = parser.jpegProcess + val exifData = ExifData( + byteOrder = parser.byteOrder, + sections = parser.sections, + mUncompressedDataPosition = parser.uncompressedDataPosition, + qualityGuess = parser.qualityGuess, + jpegProcess = parser.jpegProcess + ) val w = parser.imageWidth val h = parser.imageLength @@ -51,33 +52,34 @@ internal class ExifReader(private val mInterface : ExifInterface) { exifData.setImageSize(w, h) } - var tag : ExifTag? - var event = parser.next() while(event != ExifParser.EVENT_END) { when(event) { - ExifParser.EVENT_START_OF_IFD -> exifData.addIfdData(IfdData(parser.currentIfd)) + + ExifParser.EVENT_START_OF_IFD -> + exifData.addIfdData(IfdData(parser.currentIfd)) ExifParser.EVENT_NEW_TAG -> { - tag = parser.tag - - - - if(! tag !!.hasValue()) { - parser.registerForTagValue(tag) - } else { - // Log.v(TAG, "parsing id " + tag.getTagId() + " = " + tag); - if(parser.isDefinedTag(tag.ifd, tag.tagId.toInt())) { - exifData.getIfdData(tag.ifd) !!.setTag(tag) - } else { + val tag = parser.tag + when { + tag == null -> + Log.w(TAG, "parser.tag is null") + + ! tag.hasValue -> + parser.registerForTagValue(tag) + + ! parser.isDefinedTag(tag.ifd, tag.tagId) -> Log.w(TAG, "skip tag because not registered in the tag table:$tag") - } + + else -> + exifData.getIfdData(tag.ifd)?.setTag(tag) } + } ExifParser.EVENT_VALUE_OF_REGISTERED_TAG -> { - tag = parser.tag - if(tag !!.dataType == ExifTag.TYPE_UNDEFINED) { + val tag = parser.tag !! + if(tag.dataType == ExifTag.TYPE_UNDEFINED) { parser.readFullTagValue(tag) } exifData.getIfdData(tag.ifd) !!.setTag(tag) diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt index aea52276..ae31336d 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/ExifTag.kt @@ -52,11 +52,11 @@ open class ExifTag internal constructor( // The ifd that this tag should be put in. the ID of the IFD this tag belongs to. /* - * @see IfdId.TYPE_IFD_0 - * @see IfdId.TYPE_IFD_1 - * @see IfdId.TYPE_IFD_EXIF - * @see IfdId.TYPE_IFD_GPS - * @see IfdId.TYPE_IFD_INTEROPERABILITY + * @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, @@ -69,7 +69,7 @@ open class ExifTag internal constructor( */ // TODO: fix integer overflows with this - var componentCount : Int = 0 + var componentCount : Int = componentCount private set // The value (array of elements of type Tag Type) @@ -83,7 +83,6 @@ open class ExifTag internal constructor( val dataSize : Int get() = componentCount * getElementSize(dataType) - /** * Gets the value as a byte array. This method should be used for tags of * type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE]. @@ -124,9 +123,9 @@ open class ExifTag internal constructor( */ // Truncates val valueAsInts : IntArray? - get() = when(val v = mValue){ - is LongArray-> IntArray(v.size){ v[it].toInt()} - else ->null + get() = when(val v = mValue) { + is LongArray -> IntArray(v.size) { v[it].toInt() } + else -> null } /** @@ -143,7 +142,6 @@ open class ExifTag internal constructor( else -> null } - /** * Gets the [.TYPE_ASCII] data. * @@ -159,11 +157,6 @@ open class ExifTag internal constructor( val stringByte : ByteArray? get() = mValue as? ByteArray - init { - this.componentCount = componentCount - mValue = null - } - /** * Sets the component count of this tag. Call this function before * setValue() if the length of value does not match the component count. @@ -176,9 +169,8 @@ open class ExifTag internal constructor( * Returns true if this ExifTag contains value; otherwise, this tag will * contain an offset value that is determined when the tag is written. */ - fun hasValue() : Boolean { - return mValue != null - } + val hasValue :Boolean + get() = mValue != null /** * Sets integer values into this tag. This method should be used for tags of @@ -225,9 +217,7 @@ open class ExifTag internal constructor( * * The component count in the definition of this tag is not 1. * */ - fun setValue(value : Int) : Boolean { - return setValue(intArrayOf(value)) - } + fun setValue(value : Int) = setValue(intArrayOf(value)) /** * Sets long values into this tag. This method should be used for tags of @@ -260,9 +250,7 @@ open class ExifTag internal constructor( * * The component count in the definition for this tag is not 1. * */ - fun setValue(value : Long) : Boolean { - return setValue(longArrayOf(value)) - } + fun setValue(value : Long) = setValue(longArrayOf(value)) /** * Sets Rational values into this tag. This method should be used for tags @@ -309,8 +297,7 @@ open class ExifTag internal constructor( * * @see Rational */ - fun setValue(value : Rational) : Boolean = - setValue(arrayOf(value)) + fun setValue(value : Rational) =setValue(arrayOf(value)) /** * Sets byte values into this tag. This method should be used for tags of @@ -332,7 +319,7 @@ open class ExifTag internal constructor( return false } componentCount = length - mValue = ByteArray(length).also{ + mValue = ByteArray(length).also { System.arraycopy(value, offset, it, 0, length) } return true @@ -348,8 +335,7 @@ open class ExifTag internal constructor( * * The component count in the definition for this tag is not 1. * */ - fun setValue(value : Byte) : Boolean = - setValue(byteArrayOf(value)) + fun setValue(value : Byte) = setValue(byteArrayOf(value)) /** * Sets the value for this tag using an appropriate setValue method for the @@ -375,31 +361,33 @@ open class ExifTag internal constructor( is Int -> return setValue(obj.toInt()) is Long -> return setValue(obj.toLong()) - else ->{ - + else -> { + @Suppress("UNCHECKED_CAST") val ra = obj as? Array - if(ra != null) return setValue( ra ) + if(ra != null) return setValue(ra) // Nulls in this array are treated as zeroes. @Suppress("UNCHECKED_CAST") val sa = obj as? Array - if( sa != null) return setValue(IntArray(sa.size){ (sa[it]?.toInt() ?: 0) and 0xffff}) + 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 - if( ia != null) return setValue(IntArray(ia.size){ ia[it] ?: 0 }) + 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 - if( la != null) return setValue(LongArray(la.size){ la[it] ?: 0L }) + 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 - if( ba != null) return setValue(ByteArray(ba.size){ ba[it] ?: 0 }) + if(ba != null) return setValue(ByteArray(ba.size) { ba[it] ?: 0 }) return false } @@ -487,7 +475,7 @@ open class ExifTag internal constructor( */ fun getValueAsByte(defaultValue : Byte) : Byte { val array = valueAsBytes - return if(array?.isNotEmpty()==true) array[0] else defaultValue + return if(array?.isNotEmpty() == true) array[0] else defaultValue } /** @@ -501,7 +489,7 @@ open class ExifTag internal constructor( * @return the tag's value as a Rational, or the defaultValue. */ fun getValueAsRational(defaultValue : Long) : Rational = - getValueAsRational( Rational(defaultValue, 1)) + getValueAsRational(Rational(defaultValue, 1)) /** * Gets the value as a Rational. If there are more than 1 Rationals in this @@ -514,7 +502,7 @@ open class ExifTag internal constructor( */ private fun getValueAsRational(defaultValue : Rational) : Rational { val array = valueAsRationals - return if(array?.isNotEmpty()==true) array[0] else defaultValue + return if(array?.isNotEmpty() == true) array[0] else defaultValue } /** @@ -528,7 +516,7 @@ open class ExifTag internal constructor( */ fun getValueAsInt(defaultValue : Int) : Int { val array = valueAsInts - return if(array?.isNotEmpty()==true) array[0] else defaultValue + return if(array?.isNotEmpty() == true) array[0] else defaultValue } /** @@ -542,7 +530,7 @@ open class ExifTag internal constructor( */ fun getValueAsLong(defaultValue : Long) : Long { val array = valueAsLongs - return if(array?.isNotEmpty()==true) array[0] else defaultValue + return if(array?.isNotEmpty() == true) array[0] else defaultValue } /** @@ -636,54 +624,24 @@ open class ExifTag internal constructor( var hasDefinedCount : Boolean get() = mHasDefinedDefaultComponentCount - set(value){ + set(value) { mHasDefinedDefaultComponentCount = value } - private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean { - for(v in value) { - if(v > UNSIGNED_SHORT_MAX || v < 0) { - return true - } - } - return false - } + private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean = + null != value.find { it !in 0 .. UNSIGNED_SHORT_MAX } - private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean { - for(v in value) { - if(v < 0 || v > UNSIGNED_LONG_MAX) { - return true - } - } - return false - } + private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean = + null != value.find { it !in 0 .. UNSIGNED_LONG_MAX } - private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean { - for(v in value) { - if(v < 0) { - return true - } - } - return false - } + private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean = + null != value.find { it < 0 } - private fun checkOverflowForUnsignedRational(value : Array) : Boolean { - for(v in value) { - if(v.numerator < 0 || v.denominator < 0 || v.numerator > UNSIGNED_LONG_MAX || v.denominator > UNSIGNED_LONG_MAX) { - return true - } - } - return false - } + private fun checkOverflowForUnsignedRational(value : Array) : Boolean = + null != value.find { it.numerator !in 0 .. UNSIGNED_LONG_MAX || it.denominator !in 0 .. UNSIGNED_LONG_MAX } - private fun checkOverflowForRational(value : Array) : Boolean { - for(v in value) { - if(v.numerator < LONG_MIN || v.denominator < LONG_MIN || v.numerator > LONG_MAX || v.denominator > LONG_MAX) { - return true - } - } - return false - } + private fun checkOverflowForRational(value : Array) : 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() @@ -766,8 +724,6 @@ open class ExifTag internal constructor( } } - - companion object { /** * The BYTE type in the EXIF standard. An 8-bit unsigned integer. @@ -832,11 +788,11 @@ open class ExifTag internal constructor( */ fun isValidIfd(ifdId : Int) : Boolean = when(ifdId) { - IfdId.TYPE_IFD_0, - IfdId.TYPE_IFD_1, - IfdId.TYPE_IFD_EXIF, - IfdId.TYPE_IFD_INTEROPERABILITY, - IfdId.TYPE_IFD_GPS -> true + IfdData.TYPE_IFD_0, + IfdData.TYPE_IFD_1, + IfdData.TYPE_IFD_EXIF, + IfdData.TYPE_IFD_INTEROPERABILITY, + IfdData.TYPE_IFD_GPS -> true else -> false } @@ -883,11 +839,4 @@ open class ExifTag internal constructor( else -> "" } } - } -/** - * Equivalent to setValue(value, 0, value.length). - */ -/** - * Equivalent to getBytes(buffer, 0, buffer.length). - */ diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt index 981137d0..4e62636a 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/IfdData.kt @@ -18,35 +18,33 @@ package it.sephiroth.android.library.exif2 import java.util.HashMap - -/** - * The constants of the IFD ID defined in EXIF spec. - */ -object IfdId { - const val TYPE_IFD_0 = 0 - const val TYPE_IFD_1 = 1 - const val TYPE_IFD_EXIF = 2 - const val TYPE_IFD_INTEROPERABILITY = 3 - const val TYPE_IFD_GPS = 4 - /* This is used in ExifData to allocate enough IfdData */ - const val TYPE_IFD_COUNT = 5 - - val list = intArrayOf( - TYPE_IFD_0, - TYPE_IFD_1, - TYPE_IFD_EXIF, - TYPE_IFD_INTEROPERABILITY, - TYPE_IFD_GPS - ) -} - - // This class stores all the tags in an IFD. // an IfdData with given IFD ID. internal class IfdData( - // the ID of this IFD. - val id : Int + val id : Int // the ID of this IFD. ) { + + companion object { + /** + * The constants of the IFD ID defined in EXIF spec. + */ + const val TYPE_IFD_0 = 0 + const val TYPE_IFD_1 = 1 + const val TYPE_IFD_EXIF = 2 + const val TYPE_IFD_INTEROPERABILITY = 3 + const val TYPE_IFD_GPS = 4 + /* This is used in ExifData to allocate enough IfdData */ + const val TYPE_IFD_COUNT = 5 + + val list = intArrayOf( + TYPE_IFD_0, + TYPE_IFD_1, + TYPE_IFD_EXIF, + TYPE_IFD_INTEROPERABILITY, + TYPE_IFD_GPS + ) + } + private val mExifTags = HashMap() // the offset of next IFD. @@ -56,9 +54,9 @@ internal class IfdData( val tagCount : Int get() = mExifTags.size - // a array the contains all [ExifTag] in this IFD. - val allTags : Array - get() = mExifTags.values.toTypedArray() + // Collection the contains all [ExifTag] in this IFD. + val allTagsCollection : Collection + get() = mExifTags.values // checkCollision fun contains(tagId : Short) : Boolean { @@ -87,19 +85,12 @@ internal class IfdData( * IFDs offset or thumbnail offset will be ignored. */ override fun equals(other : Any?) : Boolean { - if(other === null) return false - if(other === this) return true if(other is IfdData) { + if(other === this) return true if(other.id == id && other.tagCount == tagCount) { - val tags = other.allTags - for(tag in tags) { - if(ExifInterface.isOffsetTag(tag.tagId)) { - continue - } - val tag2 = mExifTags[tag.tagId] - if(tag != tag2) { - return false - } + for(tag in other.allTagsCollection) { + if(ExifInterface.isOffsetTag(tag.tagId)) continue + if(tag != mExifTags[tag.tagId]) return false } return true } diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt index c9cc52e8..bdede84f 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/Rational.kt @@ -30,6 +30,7 @@ class Rational( ) { // copy from a Rational. + @Suppress("unused") constructor(r : Rational) : this( numerator = r.numerator, denominator = r.denominator diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/CountedDataInputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt similarity index 98% rename from exif/src/main/java/it/sephiroth/android/library/exif2/CountedDataInputStream.kt rename to exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt index af9dca5f..b57801c7 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/CountedDataInputStream.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/CountedDataInputStream.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package it.sephiroth.android.library.exif2 +package it.sephiroth.android.library.exif2.utils import java.io.EOFException import java.io.FilterInputStream diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/OrderedDataOutputStream.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt similarity index 94% rename from exif/src/main/java/it/sephiroth/android/library/exif2/OrderedDataOutputStream.kt rename to exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt index 817c9246..104502c4 100644 --- a/exif/src/main/java/it/sephiroth/android/library/exif2/OrderedDataOutputStream.kt +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/OrderedDataOutputStream.kt @@ -14,8 +14,9 @@ * limitations under the License. */ -package it.sephiroth.android.library.exif2 +package it.sephiroth.android.library.exif2.utils +import it.sephiroth.android.library.exif2.Rational import java.io.FilterOutputStream import java.io.IOException import java.io.OutputStream diff --git a/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt new file mode 100644 index 00000000..26416c9f --- /dev/null +++ b/exif/src/main/java/it/sephiroth/android/library/exif2/utils/Utils.kt @@ -0,0 +1,7 @@ +package it.sephiroth.android.library.exif2.utils + +internal fun Collection?.notEmpty() : Collection? = + if(this?.isNotEmpty() == true) this else null + +internal fun List?.notEmpty() : List? = + if(this?.isNotEmpty() == true) this else null diff --git a/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt b/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt index b797b551..73bd1d29 100644 --- a/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt +++ b/exif/src/test/java/it/sephiroth/android/library/exif2/Test1.kt @@ -2,8 +2,7 @@ package it.sephiroth.android.library.exif2 import android.util.Log import android.util.SparseIntArray -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue +import org.junit.Assert.* import org.junit.Test import java.io.File import java.io.FileInputStream @@ -12,7 +11,7 @@ class Test1 { @Test fun testLog() { - Log.v("TEST","test") + Log.v("TEST", "test") assertTrue("using android.util.Log", true) } @@ -32,29 +31,48 @@ class Test1 { } } - private fun getOrientation(fileName : String) : Pair = - try{ - val o = FileInputStream(getFile(fileName)).use { inStream -> - ExifInterface() - .apply { - readExif( - inStream, - ExifInterface.Options.OPTION_IFD_0 - or ExifInterface.Options.OPTION_IFD_1 - or ExifInterface.Options.OPTION_IFD_EXIF - ) - } - .getTagIntValue(ExifInterface.TAG_ORIENTATION) + private fun getOrientation(fileName : String) : Pair = + try { + val o = FileInputStream(getFile(fileName)).use { inStream -> + ExifInterface() + .apply { + readExif( + inStream, + ExifInterface.Options.OPTION_IFD_0 + or ExifInterface.Options.OPTION_IFD_1 + or ExifInterface.Options.OPTION_IFD_EXIF + ) + } + .getTagIntValue(ExifInterface.TAG_ORIENTATION) + } + Pair(o, null) + } catch(ex : Throwable) { + Pair(null, ex) + } + + private fun getThumbnailBytes(fileName : String) : Pair = + try { + val o = FileInputStream(getFile(fileName)).use { inStream -> + ExifInterface() + .apply { + readExif( + inStream, + ExifInterface.Options.OPTION_IFD_0 + or ExifInterface.Options.OPTION_IFD_1 + or ExifInterface.Options.OPTION_IFD_EXIF + ) + } + .thumbnailBytes + } + Pair(o, null) + } catch(ex : Throwable) { + Pair(null, ex) } - Pair(o,null) - }catch(ex:Throwable){ - Pair(null,ex) - } private fun testNotJpegSub(fileName : String) { - val(o,ex) = getOrientation(fileName) - assertTrue("testNotJpegSub",o ==null && ex!=null) - if( ex!= null) println("exception raised: ${ex::class.java} ${ex.message}") + val (o, ex) = getOrientation(fileName) + assertTrue("testNotJpegSub", o == null && ex != null) + if(ex != null) println("exception raised: ${ex::class.java} ${ex.message}") } @Test @@ -66,23 +84,32 @@ class Test1 { @Test fun testJpeg() { - var fileName :String - var rv : Pair - - // this file has orientation 1 - fileName = "test1.jpg" - rv = getOrientation(fileName) - assertEquals(fileName,1,rv.first) - - // this file has no orientation, it raises exception. - fileName = "test2.jpg" - rv = getOrientation(fileName) - assertTrue(fileName,rv.second != null) + var fileName : String + var rvO : Pair + var rvT : Pair // this file has orientation 6. fileName = "test3.jpg" - rv = getOrientation(fileName) - assertEquals(fileName,rv.first , 6) + rvO = getOrientation(fileName) + assertEquals(fileName, 6, rvO.first) + rvT = getThumbnailBytes(fileName) + assertNull(fileName,rvT.first) + + // this file has orientation 1 + fileName = "test1.jpg" + rvO = getOrientation(fileName) + assertEquals(fileName, 1, rvO.first) + rvT = getThumbnailBytes(fileName) + assertNull(fileName,rvT.first) + + // this file has no orientation, it raises exception. + fileName = "test2.jpg" + rvO = getOrientation(fileName) + assertNotNull(fileName, rvO.second) // + + rvT = getThumbnailBytes(fileName) + assertNotNull(fileName,rvT.second) // + } } \ No newline at end of file