refactor
This commit is contained in:
parent
c4b0c04126
commit
0c3a222fc7
|
@ -51,6 +51,8 @@
|
||||||
<w>idempotency</w>
|
<w>idempotency</w>
|
||||||
<w>ihdr</w>
|
<w>ihdr</w>
|
||||||
<w>infos</w>
|
<w>infos</w>
|
||||||
|
<w>iptc</w>
|
||||||
|
<w>jfif</w>
|
||||||
<w>kapt</w>
|
<w>kapt</w>
|
||||||
<w>kddi</w>
|
<w>kddi</w>
|
||||||
<w>kenglxn</w>
|
<w>kenglxn</w>
|
||||||
|
|
|
@ -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
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -17,6 +17,7 @@
|
||||||
package it.sephiroth.android.library.exif2
|
package it.sephiroth.android.library.exif2
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import it.sephiroth.android.library.exif2.utils.notEmpty
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException
|
import java.io.UnsupportedEncodingException
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
|
@ -33,94 +34,74 @@ import java.util.Arrays
|
||||||
* @see IfdData
|
* @see IfdData
|
||||||
*/
|
*/
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
internal open class ExifData( val byteOrder : ByteOrder ) {
|
internal class ExifData(
|
||||||
|
val byteOrder : ByteOrder = ExifInterface.DEFAULT_BYTE_ORDER,
|
||||||
|
val sections : List<ExifParser.Section> = ArrayList(),
|
||||||
|
val mUncompressedDataPosition : Int = 0,
|
||||||
|
val qualityGuess : Int = 0,
|
||||||
|
val jpegProcess : Short = 0
|
||||||
|
) {
|
||||||
|
|
||||||
var sections : List<ExifParser.Section>? = null
|
|
||||||
private val mIfdDatas = arrayOfNulls<IfdData>(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<ByteArray?>()
|
|
||||||
var qualityGuess = 0
|
|
||||||
private var imageLength = - 1
|
private var imageLength = - 1
|
||||||
private var imageWidth = - 1
|
private var imageWidth = - 1
|
||||||
var jpegProcess : Short = 0
|
|
||||||
var mUncompressedDataPosition = 0
|
|
||||||
|
|
||||||
/**
|
private val mIfdDatas = arrayOfNulls<IfdData>(IfdData.TYPE_IFD_COUNT)
|
||||||
* Gets the strip count.
|
|
||||||
*/
|
// the compressed thumbnail.
|
||||||
|
// null if there is no compressed thumbnail.
|
||||||
|
var compressedThumbnail : ByteArray? = null
|
||||||
|
|
||||||
|
private val mStripBytes = ArrayList<ByteArray?>()
|
||||||
val stripCount : Int
|
val stripCount : Int
|
||||||
get() = mStripBytes.size
|
get() = mStripBytes.size
|
||||||
|
|
||||||
/**
|
// Decodes the user comment tag into string as specified in the EXIF standard.
|
||||||
* Decodes the user comment tag into string as specified in the EXIF
|
// Returns null if decoding failed.
|
||||||
* standard. Returns null if decoding failed.
|
|
||||||
*/
|
|
||||||
val userComment : String?
|
val userComment : String?
|
||||||
get() {
|
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))
|
val tag = ifdData.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_USER_COMMENT))
|
||||||
?: return null
|
?: return null
|
||||||
if(tag.componentCount < 8) {
|
|
||||||
|
if(tag.componentCount < 8)
|
||||||
return null
|
return null
|
||||||
}
|
|
||||||
|
|
||||||
val buf = ByteArray(tag.componentCount)
|
|
||||||
tag.getBytes(buf)
|
|
||||||
|
|
||||||
val code = ByteArray(8)
|
|
||||||
System.arraycopy(buf, 0, code, 0, 8)
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
when {
|
|
||||||
code.contentEquals(USER_COMMENT_ASCII) -> String(
|
val buf = ByteArray(tag.componentCount)
|
||||||
buf,
|
tag.getBytes(buf)
|
||||||
8,
|
|
||||||
buf.size - 8,
|
val code = ByteArray(8)
|
||||||
Charsets.US_ASCII
|
System.arraycopy(buf, 0, code, 0, 8)
|
||||||
)
|
|
||||||
code.contentEquals(USER_COMMENT_JIS) -> String(
|
val charset = when {
|
||||||
buf,
|
code.contentEquals(USER_COMMENT_ASCII) -> Charsets.US_ASCII
|
||||||
8,
|
code.contentEquals(USER_COMMENT_JIS) -> eucJp
|
||||||
buf.size - 8,
|
code.contentEquals(USER_COMMENT_UNICODE) -> Charsets.UTF_16
|
||||||
Charset.forName("EUC-JP")
|
|
||||||
)
|
|
||||||
code.contentEquals(USER_COMMENT_UNICODE) -> String(
|
|
||||||
buf,
|
|
||||||
8,
|
|
||||||
buf.size - 8,
|
|
||||||
Charsets.UTF_16
|
|
||||||
)
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
if(charset == null) null else String(buf, 8, buf.size - 8, charset)
|
||||||
} catch(e : UnsupportedEncodingException) {
|
} catch(e : UnsupportedEncodingException) {
|
||||||
Log.w(TAG, "Failed to decode the user comment")
|
Log.w(TAG, "Failed to decode the user comment")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// list of all [ExifTag]s in the ExifData
|
||||||
* Returns a list of all [ExifTag]s in the ExifData or null if there
|
// or null if there are none.
|
||||||
* are none.
|
val allTags : List<ExifTag>
|
||||||
*/
|
get() = ArrayList<ExifTag>()
|
||||||
val allTags : List<ExifTag>?
|
.apply { mIfdDatas.forEach { if(it != null) addAll(it.allTagsCollection) } }
|
||||||
get() {
|
|
||||||
val ret = ArrayList<ExifTag>()
|
|
||||||
mIfdDatas.forEach { it?.allTags?.forEach { tag -> ret.add(tag) } }
|
|
||||||
return if(ret.isEmpty()) null else ret
|
|
||||||
}
|
|
||||||
|
|
||||||
val imageSize : IntArray
|
val imageSize : IntArray
|
||||||
get() = intArrayOf(imageWidth, imageLength)
|
get() = intArrayOf(imageWidth, imageLength)
|
||||||
|
|
||||||
|
val stripList : List<ByteArray>?
|
||||||
|
get() = mStripBytes.filterNotNull().notEmpty()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true it this header contains a compressed thumbnail.
|
* 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.
|
* Returns the tag with a given TID in the given IFD if the tag exists.
|
||||||
* Otherwise returns null.
|
* Otherwise returns null.
|
||||||
*/
|
*/
|
||||||
fun getTag(tag : Short, ifd : Int) : ExifTag? {
|
fun getTag(tag : Short, ifd : Int) : ExifTag? =
|
||||||
val ifdData = mIfdDatas[ifd]
|
mIfdDatas[ifd]?.getTag(tag)
|
||||||
return ifdData?.getTag(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given ExifTag to its default IFD and returns an existing ExifTag
|
* Adds the given ExifTag to its default IFD and returns an existing ExifTag
|
||||||
* with the same TID or null if none exist.
|
* with the same TID or null if none exist.
|
||||||
*/
|
*/
|
||||||
fun addTag(tag : ExifTag?) : ExifTag? {
|
fun addTag(tag : ExifTag?) : ExifTag? =
|
||||||
if(tag != null) {
|
when(tag) {
|
||||||
val ifd = tag.ifd
|
null -> null
|
||||||
return addTag(tag, ifd)
|
else -> addTag(tag, tag.ifd)
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the given ExifTag to the given IFD and returns an existing ExifTag
|
* Adds the given ExifTag to the given IFD and returns an existing ExifTag
|
||||||
* with the same TID or null if none exist.
|
* with the same TID or null if none exist.
|
||||||
*/
|
*/
|
||||||
private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? {
|
private fun addTag(tag : ExifTag?, ifdId : Int) : ExifTag? =
|
||||||
if(tag != null && ExifTag.isValidIfd(ifdId)) {
|
when {
|
||||||
val ifdData = getOrCreateIfdData(ifdId)
|
tag == null -> null
|
||||||
return ifdData.setTag(tag)
|
! ExifTag.isValidIfd(ifdId) -> null
|
||||||
|
else -> getOrCreateIfdData(ifdId).setTag(tag)
|
||||||
}
|
}
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the [IfdData] object corresponding to a given IFD or
|
* Returns the [IfdData] object corresponding to a given IFD or
|
||||||
* generates one if none exist.
|
* generates one if none exist.
|
||||||
*/
|
*/
|
||||||
private fun getOrCreateIfdData(ifdId : Int) : IfdData {
|
private fun getOrCreateIfdData(ifdId : Int) : IfdData {
|
||||||
var ifdData : IfdData? = mIfdDatas[ifdId]
|
var ifdData = mIfdDatas[ifdId]
|
||||||
if(ifdData == null) {
|
if(ifdData == null) {
|
||||||
ifdData = IfdData(ifdId)
|
ifdData = IfdData(ifdId)
|
||||||
mIfdDatas[ifdId] = ifdData
|
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.
|
* Removes the thumbnail and its related tags. IFD1 will be removed.
|
||||||
*/
|
*/
|
||||||
protected fun removeThumbnailData() {
|
fun removeThumbnailData() {
|
||||||
clearThumbnailAndStrips()
|
clearThumbnailAndStrips()
|
||||||
mIfdDatas[IfdId.TYPE_IFD_1] = null
|
mIfdDatas[IfdData.TYPE_IFD_1] = null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearThumbnailAndStrips() {
|
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
|
* Returns a list of all [ExifTag]s in a given IFD or null if there
|
||||||
* are none.
|
* are none.
|
||||||
*/
|
*/
|
||||||
fun getAllTagsForIfd(ifd : Int) : List<ExifTag>? {
|
fun getAllTagsForIfd(ifd : Int) : List<ExifTag>? =
|
||||||
val d = mIfdDatas[ifd] ?: return null
|
mIfdDatas[ifd]?.allTagsCollection?.notEmpty()?.toList()
|
||||||
val tags = d.allTags
|
|
||||||
val ret = ArrayList<ExifTag>(tags.size)
|
|
||||||
for(t in tags) {
|
|
||||||
ret.add(t)
|
|
||||||
}
|
|
||||||
return if(ret.size == 0) {
|
|
||||||
null
|
|
||||||
} else ret
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a list of all [ExifTag]s with a given TID
|
// Returns a list of all [ExifTag]s with a given TID
|
||||||
// or null if there are none.
|
// 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 ifd1 = other.getIfdData(i)
|
||||||
val ifd2 = getIfdData(i)
|
val ifd2 = getIfdData(i)
|
||||||
if(ifd1 != ifd2) return false
|
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
|
* Returns the [IfdData] object corresponding to a given IFD if it
|
||||||
* exists or null.
|
* exists or null.
|
||||||
*/
|
*/
|
||||||
fun getIfdData(ifdId : Int) : IfdData? {
|
fun getIfdData(ifdId : Int) = when {
|
||||||
return if(ExifTag.isValidIfd(ifdId)) {
|
! ExifTag.isValidIfd(ifdId) -> null
|
||||||
mIfdDatas[ifdId]
|
else -> mIfdDatas[ifdId]
|
||||||
} else null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setImageSize(imageWidth : Int, imageLength : Int) {
|
fun setImageSize(imageWidth : Int, imageLength : Int) {
|
||||||
|
@ -307,7 +273,7 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
||||||
|
|
||||||
override fun hashCode() : Int {
|
override fun hashCode() : Int {
|
||||||
var result = byteOrder.hashCode()
|
var result = byteOrder.hashCode()
|
||||||
result = 31 * result + (sections?.hashCode() ?: 0)
|
result = 31 * result + (sections.hashCode())
|
||||||
result = 31 * result + mIfdDatas.contentHashCode()
|
result = 31 * result + mIfdDatas.contentHashCode()
|
||||||
result = 31 * result + (compressedThumbnail?.contentHashCode() ?: 0)
|
result = 31 * result + (compressedThumbnail?.contentHashCode() ?: 0)
|
||||||
result = 31 * result + mStripBytes.hashCode()
|
result = 31 * result + mStripBytes.hashCode()
|
||||||
|
@ -330,5 +296,8 @@ internal open class ExifData( val byteOrder : ByteOrder ) {
|
||||||
|
|
||||||
private val USER_COMMENT_UNICODE =
|
private val USER_COMMENT_UNICODE =
|
||||||
byteArrayOf(0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00)
|
byteArrayOf(0x55, 0x4E, 0x49, 0x43, 0x4F, 0x44, 0x45, 0x00)
|
||||||
|
|
||||||
|
private val eucJp = Charset.forName("EUC-JP")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,25 +17,23 @@
|
||||||
package it.sephiroth.android.library.exif2
|
package it.sephiroth.android.library.exif2
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import it.sephiroth.android.library.exif2.utils.OrderedDataOutputStream
|
||||||
import java.io.BufferedOutputStream
|
import java.io.BufferedOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.util.ArrayList
|
import java.util.*
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
internal class ExifOutputStream(
|
||||||
/**
|
|
||||||
* Gets the Exif header to be written into the JPEF file.
|
private val mInterface : ExifInterface,
|
||||||
*/
|
|
||||||
/**
|
// the Exif header to be written into the JPEG file.
|
||||||
* Sets the ExifData to be written into the JPEG file. Should be called
|
private val exifData : ExifData
|
||||||
* before writing image data.
|
) {
|
||||||
*/
|
|
||||||
var exifData : ExifData? = null
|
|
||||||
|
|
||||||
private val mBuffer = ByteBuffer.allocate(4)
|
private val mBuffer = ByteBuffer.allocate(4)
|
||||||
|
|
||||||
private fun requestByteToBuffer(
|
private fun requestByteToBuffer(
|
||||||
|
@ -49,13 +47,9 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
fun writeExifData(out : OutputStream) {
|
fun writeExifData(out : OutputStream) {
|
||||||
if(exifData == null) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.v(TAG, "Writing exif data...")
|
Log.v(TAG, "Writing exif data...")
|
||||||
|
|
||||||
val nullTags = stripNullValueTags(exifData !!)
|
val nullTags = stripNullValueTags(exifData)
|
||||||
createRequiredIfdAndTag()
|
createRequiredIfdAndTag()
|
||||||
val exifSize = calculateAllOffset()
|
val exifSize = calculateAllOffset()
|
||||||
// Log.i(TAG, "exifSize: " + (exifSize + 8));
|
// Log.i(TAG, "exifSize: " + (exifSize + 8));
|
||||||
|
@ -64,7 +58,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
val outputStream = BufferedOutputStream(out, STREAMBUFFER_SIZE)
|
val outputStream = BufferedOutputStream(out, STREAMBUFFER_SIZE)
|
||||||
val dataOutputStream = OrderedDataOutputStream(outputStream)
|
val dataOutputStream =
|
||||||
|
OrderedDataOutputStream(outputStream)
|
||||||
|
|
||||||
dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN)
|
dataOutputStream.setByteOrder(ByteOrder.BIG_ENDIAN)
|
||||||
|
|
||||||
|
@ -73,12 +68,12 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
dataOutputStream.writeShort((exifSize + 8).toShort())
|
dataOutputStream.writeShort((exifSize + 8).toShort())
|
||||||
dataOutputStream.writeInt(EXIF_HEADER)
|
dataOutputStream.writeInt(EXIF_HEADER)
|
||||||
dataOutputStream.writeShort(0x0000.toShort())
|
dataOutputStream.writeShort(0x0000.toShort())
|
||||||
if(exifData !!.byteOrder == ByteOrder.BIG_ENDIAN) {
|
if(exifData.byteOrder == ByteOrder.BIG_ENDIAN) {
|
||||||
dataOutputStream.writeShort(TIFF_BIG_ENDIAN)
|
dataOutputStream.writeShort(TIFF_BIG_ENDIAN)
|
||||||
} else {
|
} else {
|
||||||
dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN)
|
dataOutputStream.writeShort(TIFF_LITTLE_ENDIAN)
|
||||||
}
|
}
|
||||||
dataOutputStream.setByteOrder(exifData !!.byteOrder)
|
dataOutputStream.setByteOrder(exifData.byteOrder)
|
||||||
dataOutputStream.writeShort(TIFF_HEADER)
|
dataOutputStream.writeShort(TIFF_HEADER)
|
||||||
dataOutputStream.writeInt(8)
|
dataOutputStream.writeInt(8)
|
||||||
writeAllTags(dataOutputStream)
|
writeAllTags(dataOutputStream)
|
||||||
|
@ -86,57 +81,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
writeThumbnail(dataOutputStream)
|
writeThumbnail(dataOutputStream)
|
||||||
|
|
||||||
for(t in nullTags) {
|
for(t in nullTags) {
|
||||||
exifData !!.addTag(t)
|
exifData.addTag(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
dataOutputStream.flush()
|
dataOutputStream.flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stripNullValueTags(data : ExifData) : ArrayList<ExifTag> {
|
// strip tags that has null value
|
||||||
val nullTags = ArrayList<ExifTag>()
|
// return list of removed tags
|
||||||
for(t in data.allTags !!) {
|
private fun stripNullValueTags(data : ExifData) =
|
||||||
if(t.getValue() == null && ! ExifInterface.isOffsetTag(t.tagId)) {
|
ArrayList<ExifTag>()
|
||||||
data.removeTag(t.tagId, t.ifd)
|
.apply {
|
||||||
nullTags.add(t)
|
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)
|
@Throws(IOException::class)
|
||||||
private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) {
|
private fun writeThumbnail(dataOutputStream : OrderedDataOutputStream) {
|
||||||
if(exifData !!.hasCompressedThumbnail()) {
|
val compressedThumbnail = exifData.compressedThumbnail
|
||||||
|
if(compressedThumbnail != null) {
|
||||||
Log.d(TAG, "writing thumbnail..")
|
Log.d(TAG, "writing thumbnail..")
|
||||||
dataOutputStream.write(exifData !!.compressedThumbnail !!)
|
dataOutputStream.write(compressedThumbnail)
|
||||||
} else if(exifData !!.hasUncompressedStrip()) {
|
} else {
|
||||||
Log.d(TAG, "writing uncompressed strip..")
|
val stripList = exifData.stripList
|
||||||
for(i in 0 until exifData !!.stripCount) {
|
if(stripList != null) {
|
||||||
dataOutputStream.write(exifData !!.getStrip(i) !!)
|
Log.d(TAG, "writing uncompressed strip..")
|
||||||
|
stripList.forEach {
|
||||||
|
dataOutputStream.write(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun writeAllTags(dataOutputStream : OrderedDataOutputStream) {
|
private fun writeAllTags(dataOutputStream : OrderedDataOutputStream) {
|
||||||
writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_0) !!, dataOutputStream)
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_0), dataOutputStream)
|
||||||
writeIfd(exifData !!.getIfdData(IfdId.TYPE_IFD_EXIF) !!, dataOutputStream)
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_EXIF), dataOutputStream)
|
||||||
val interoperabilityIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY)
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY), dataOutputStream)
|
||||||
if(interoperabilityIfd != null) {
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_GPS), dataOutputStream)
|
||||||
writeIfd(interoperabilityIfd, dataOutputStream)
|
writeIfd(exifData.getIfdData(IfdData.TYPE_IFD_1), 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun writeIfd(ifd : IfdData, dataOutputStream : OrderedDataOutputStream) {
|
private fun writeIfd(ifd : IfdData?, dataOutputStream : OrderedDataOutputStream) {
|
||||||
val tags = ifd.allTags
|
ifd ?: return
|
||||||
|
val tags = ifd.allTagsCollection
|
||||||
dataOutputStream.writeShort(tags.size.toShort())
|
dataOutputStream.writeShort(tags.size.toShort())
|
||||||
for(tag in tags) {
|
for(tag in tags) {
|
||||||
dataOutputStream.writeShort(tag.tagId)
|
dataOutputStream.writeShort(tag.tagId)
|
||||||
|
@ -166,8 +159,8 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
private fun calculateOffsetOfIfd(ifd : IfdData, offsetArg : Int) : Int {
|
private fun calculateOffsetOfIfd(ifd : IfdData, offsetArg : Int) : Int {
|
||||||
var offset = offsetArg
|
var offset = offsetArg
|
||||||
offset += 2 + ifd.tagCount * TAG_SIZE + 4
|
offset += 2 + ifd.tagCount * TAG_SIZE + 4
|
||||||
val tags = ifd.allTags
|
|
||||||
for(tag in tags) {
|
for(tag in ifd.allTagsCollection) {
|
||||||
if(tag.dataSize > 4) {
|
if(tag.dataSize > 4) {
|
||||||
tag.offset = offset
|
tag.offset = offset
|
||||||
offset += tag.dataSize
|
offset += tag.dataSize
|
||||||
|
@ -178,49 +171,53 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
private fun createRequiredIfdAndTag() {
|
private fun createRequiredIfdAndTag() {
|
||||||
|
|
||||||
// IFD0 is required for all file
|
// 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) {
|
if(ifd0 == null) {
|
||||||
ifd0 = IfdData(IfdId.TYPE_IFD_0)
|
ifd0 = IfdData(IfdData.TYPE_IFD_0)
|
||||||
exifData !!.addIfdData(ifd0)
|
exifData.addIfdData(ifd0)
|
||||||
}
|
}
|
||||||
|
|
||||||
val exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD)
|
val exifOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_EXIF_IFD)
|
||||||
?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_EXIF_IFD)
|
?: throw IOException("No definition for crucial exif tag: " + ExifInterface.TAG_EXIF_IFD)
|
||||||
ifd0.setTag(exifOffsetTag)
|
ifd0.setTag(exifOffsetTag)
|
||||||
|
|
||||||
// Exif IFD is required for all files.
|
// 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) {
|
if(exifIfd == null) {
|
||||||
exifIfd = IfdData(IfdId.TYPE_IFD_EXIF)
|
exifIfd = IfdData(IfdData.TYPE_IFD_EXIF)
|
||||||
exifData !!.addIfdData(exifIfd)
|
exifData.addIfdData(exifIfd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GPS IFD
|
// GPS IFD
|
||||||
val gpsIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_GPS)
|
val gpsIfd = exifData.getIfdData(IfdData.TYPE_IFD_GPS)
|
||||||
if(gpsIfd != null) {
|
if(gpsIfd != null) {
|
||||||
val gpsOffsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_GPS_IFD)
|
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)
|
ifd0.setTag(gpsOffsetTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interoperability IFD
|
// Interoperability IFD
|
||||||
val interIfd = exifData !!.getIfdData(IfdId.TYPE_IFD_INTEROPERABILITY)
|
val interIfd = exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)
|
||||||
if(interIfd != null) {
|
if(interIfd != null) {
|
||||||
val interOffsetTag =
|
val interOffsetTag =
|
||||||
mInterface.buildUninitializedTag(ExifInterface.TAG_INTEROPERABILITY_IFD)
|
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)
|
exifIfd.setTag(interOffsetTag)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ifd1 = exifData !!.getIfdData(IfdId.TYPE_IFD_1)
|
var ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)
|
||||||
|
|
||||||
// thumbnail
|
// thumbnail
|
||||||
|
val compressedThumbnail = exifData.compressedThumbnail
|
||||||
|
val stripList = exifData.stripList
|
||||||
when {
|
when {
|
||||||
exifData !!.hasCompressedThumbnail() -> {
|
|
||||||
|
compressedThumbnail != null -> {
|
||||||
if(ifd1 == null) {
|
if(ifd1 == null) {
|
||||||
ifd1 = IfdData(IfdId.TYPE_IFD_1)
|
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
||||||
exifData !!.addIfdData(ifd1)
|
exifData.addIfdData(ifd1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val offsetTag =
|
val offsetTag =
|
||||||
|
@ -232,34 +229,35 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
|
mInterface.buildUninitializedTag(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH)
|
||||||
?: throw IOException("No definition for crucial exif tag: ${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)
|
ifd1.setTag(lengthTag)
|
||||||
|
|
||||||
// Get rid of tags for uncompressed if they exist.
|
// Get rid of tags for uncompressed if they exist.
|
||||||
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
||||||
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS))
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_BYTE_COUNTS))
|
||||||
}
|
}
|
||||||
exifData !!.hasUncompressedStrip() -> {
|
|
||||||
|
stripList != null -> {
|
||||||
if(ifd1 == null) {
|
if(ifd1 == null) {
|
||||||
ifd1 = IfdData(IfdId.TYPE_IFD_1)
|
ifd1 = IfdData(IfdData.TYPE_IFD_1)
|
||||||
exifData !!.addIfdData(ifd1)
|
exifData.addIfdData(ifd1)
|
||||||
}
|
}
|
||||||
val stripCount = exifData !!.stripCount
|
|
||||||
val offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS)
|
val offsetTag = mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_OFFSETS)
|
||||||
?: throw IOException("No definition for crucial exif tag: ${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)
|
val lengthTag =
|
||||||
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}")
|
mInterface.buildUninitializedTag(ExifInterface.TAG_STRIP_BYTE_COUNTS)
|
||||||
val lengths = LongArray(stripCount)
|
?: throw IOException("No definition for crucial exif tag: ${ExifInterface.TAG_STRIP_BYTE_COUNTS}")
|
||||||
for(i in 0 until exifData !!.stripCount) {
|
|
||||||
lengths[i] = exifData !!.getStrip(i) !!.size.toLong()
|
val bytesList = LongArray(stripList.size)
|
||||||
}
|
stripList.forEachIndexed { index, bytes -> bytesList[index] = bytes.size.toLong() }
|
||||||
lengthTag.setValue(lengths)
|
lengthTag.setValue(bytesList)
|
||||||
ifd1.setTag(offsetTag)
|
ifd1.setTag(offsetTag)
|
||||||
ifd1.setTag(lengthTag)
|
ifd1.setTag(lengthTag)
|
||||||
// Get rid of tags for compressed if they exist.
|
// 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))
|
||||||
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT_LENGTH))
|
||||||
}
|
}
|
||||||
|
|
||||||
ifd1 != null -> {
|
ifd1 != null -> {
|
||||||
// Get rid of offset and length tags if there is no thumbnail.
|
// Get rid of offset and length tags if there is no thumbnail.
|
||||||
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
ifd1.removeTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_STRIP_OFFSETS))
|
||||||
|
@ -272,50 +270,55 @@ internal class ExifOutputStream(private val mInterface : ExifInterface) {
|
||||||
|
|
||||||
private fun calculateAllOffset() : Int {
|
private fun calculateAllOffset() : Int {
|
||||||
var offset = TIFF_HEADER_SIZE.toInt()
|
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)
|
val ifd0 = exifData.getIfdData(IfdData.TYPE_IFD_0)?.also {
|
||||||
offset = calculateOffsetOfIfd(exifIfd !!, offset)
|
offset = calculateOffsetOfIfd(it, 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 gpsIfd = exifData.getIfdData(IfdId.TYPE_IFD_GPS)
|
val exifIfd = exifData.getIfdData(IfdData.TYPE_IFD_EXIF)?.also { it ->
|
||||||
if(gpsIfd != null) {
|
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_EXIF_IFD))
|
||||||
ifd0.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
|
|
||||||
?.setValue(offset)
|
?.setValue(offset)
|
||||||
offset = calculateOffsetOfIfd(gpsIfd, offset)
|
offset = calculateOffsetOfIfd(it, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ifd1 = exifData.getIfdData(IfdId.TYPE_IFD_1)
|
exifData.getIfdData(IfdData.TYPE_IFD_INTEROPERABILITY)?.also { it ->
|
||||||
if(ifd1 != null) {
|
exifIfd?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_INTEROPERABILITY_IFD))
|
||||||
ifd0.offsetToNextIfd = offset
|
?.setValue(offset)
|
||||||
offset = calculateOffsetOfIfd(ifd1, offset)
|
offset = calculateOffsetOfIfd(it, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
// thumbnail
|
exifData.getIfdData(IfdData.TYPE_IFD_GPS)?.also {
|
||||||
if(exifData .hasCompressedThumbnail()) {
|
ifd0?.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_GPS_IFD))
|
||||||
ifd1 !!.getTag(ExifInterface.getTrueTagKey(ExifInterface.TAG_JPEG_INTERCHANGE_FORMAT))
|
|
||||||
?.setValue(offset)
|
?.setValue(offset)
|
||||||
offset += exifData .compressedThumbnail !!.size
|
offset = calculateOffsetOfIfd(it, offset)
|
||||||
} else if(exifData .hasUncompressedStrip()) {
|
}
|
||||||
val stripCount = exifData .stripCount
|
|
||||||
val offsets = LongArray(stripCount)
|
val ifd1 = exifData.getIfdData(IfdData.TYPE_IFD_1)?.also {
|
||||||
for(i in 0 until exifData .stripCount) {
|
ifd0?.offsetToNextIfd = offset
|
||||||
offsets[i] = offset.toLong()
|
offset = calculateOffsetOfIfd(it, offset)
|
||||||
offset += exifData .getStrip(i) !!.size
|
}
|
||||||
|
|
||||||
|
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
|
return offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package it.sephiroth.android.library.exif2
|
package it.sephiroth.android.library.exif2
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import it.sephiroth.android.library.exif2.utils.CountedDataInputStream
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
|
@ -40,15 +41,15 @@ private constructor(
|
||||||
/**
|
/**
|
||||||
* the ID of current IFD.
|
* the ID of current IFD.
|
||||||
*
|
*
|
||||||
* @see IfdId.TYPE_IFD_0
|
* @see IfdData.TYPE_IFD_0
|
||||||
* @see IfdId.TYPE_IFD_1
|
* @see IfdData.TYPE_IFD_1
|
||||||
* @see IfdId.TYPE_IFD_GPS
|
* @see IfdData.TYPE_IFD_GPS
|
||||||
* @see IfdId.TYPE_IFD_INTEROPERABILITY
|
* @see IfdData.TYPE_IFD_INTEROPERABILITY
|
||||||
* @see IfdId.TYPE_IFD_EXIF
|
* @see IfdData.TYPE_IFD_EXIF
|
||||||
*/
|
*/
|
||||||
var currentIfd : Int = 0
|
var currentIfd : Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If [.next] return [.EVENT_NEW_TAG] or
|
* If [.next] return [.EVENT_NEW_TAG] or
|
||||||
* [.EVENT_VALUE_OF_REGISTERED_TAG], call this function to get the
|
* [.EVENT_VALUE_OF_REGISTERED_TAG], call this function to get the
|
||||||
|
@ -154,10 +155,10 @@ private constructor(
|
||||||
throw ExifInvalidFormatException("Invalid offset $offset")
|
throw ExifInvalidFormatException("Invalid offset $offset")
|
||||||
}
|
}
|
||||||
mIfd0Position = offset.toInt()
|
mIfd0Position = offset.toInt()
|
||||||
currentIfd = IfdId.TYPE_IFD_0
|
currentIfd = IfdData.TYPE_IFD_0
|
||||||
|
|
||||||
if(isIfdRequested(IfdId.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
|
if(isIfdRequested(IfdData.TYPE_IFD_0) || needToParseOffsetsInCurrentIfd()) {
|
||||||
registerIfd(IfdId.TYPE_IFD_0, offset)
|
registerIfd(IfdData.TYPE_IFD_0, offset)
|
||||||
if(offset != DEFAULT_IFD0_OFFSET.toLong()) {
|
if(offset != DEFAULT_IFD0_OFFSET.toLong()) {
|
||||||
val ba = ByteArray(offset.toInt() - DEFAULT_IFD0_OFFSET)
|
val ba = ByteArray(offset.toInt() - DEFAULT_IFD0_OFFSET)
|
||||||
mDataAboveIfd0 = ba
|
mDataAboveIfd0 = ba
|
||||||
|
@ -182,15 +183,16 @@ private constructor(
|
||||||
|
|
||||||
@Throws(IOException::class, ExifInvalidFormatException::class)
|
@Throws(IOException::class, ExifInvalidFormatException::class)
|
||||||
private fun seekTiffData(inputStream : InputStream) : CountedDataInputStream {
|
private fun seekTiffData(inputStream : InputStream) : CountedDataInputStream {
|
||||||
val dataStream = CountedDataInputStream(inputStream)
|
val dataStream =
|
||||||
|
CountedDataInputStream(inputStream)
|
||||||
var tiffStream : CountedDataInputStream? = null
|
var tiffStream : CountedDataInputStream? = null
|
||||||
|
|
||||||
var a = dataStream.readUnsignedByte()
|
var a = dataStream.readUnsignedByte()
|
||||||
val b = dataStream.readUnsignedByte()
|
val b = dataStream.readUnsignedByte()
|
||||||
|
|
||||||
|
|
||||||
if(a == 137 && b == 80) error("maybe PNG image")
|
if(a == 137 && b == 80) error("maybe PNG image")
|
||||||
|
|
||||||
if(a != 0xFF || b != JpegHeader.TAG_SOI) error("invalid jpeg header")
|
if(a != 0xFF || b != JpegHeader.TAG_SOI) error("invalid jpeg header")
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
|
@ -213,9 +215,6 @@ private constructor(
|
||||||
Log.w(TAG, "Extraneous ${a - 1} padding bytes before section $marker")
|
Log.w(TAG, "Extraneous ${a - 1} padding bytes before section $marker")
|
||||||
}
|
}
|
||||||
|
|
||||||
val section = Section()
|
|
||||||
section.type = marker
|
|
||||||
|
|
||||||
// Read the length of the section.
|
// Read the length of the section.
|
||||||
val lh = dataStream.readByte().toInt()
|
val lh = dataStream.readByte().toInt()
|
||||||
val ll = dataStream.readByte().toInt()
|
val ll = dataStream.readByte().toInt()
|
||||||
|
@ -225,8 +224,6 @@ private constructor(
|
||||||
throw ExifInvalidFormatException("Invalid marker")
|
throw ExifInvalidFormatException("Invalid marker")
|
||||||
}
|
}
|
||||||
|
|
||||||
section.size = itemlen
|
|
||||||
|
|
||||||
data = ByteArray(itemlen)
|
data = ByteArray(itemlen)
|
||||||
data[0] = lh.toByte()
|
data[0] = lh.toByte()
|
||||||
data[1] = ll.toByte()
|
data[1] = ll.toByte()
|
||||||
|
@ -240,7 +237,7 @@ private constructor(
|
||||||
throw ExifInvalidFormatException("Premature end of file? Expecting " + (itemlen - 2) + ", received " + got)
|
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
|
var ignore = false
|
||||||
|
|
||||||
|
@ -286,7 +283,9 @@ private constructor(
|
||||||
// header = Exif, headerTail=\0\0
|
// header = Exif, headerTail=\0\0
|
||||||
if(header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
|
if(header == EXIF_HEADER && headerTail == EXIF_HEADER_TAIL) {
|
||||||
tiffStream =
|
tiffStream =
|
||||||
CountedDataInputStream(ByteArrayInputStream(data, 8, itemlen - 8))
|
CountedDataInputStream(
|
||||||
|
ByteArrayInputStream(data, 8, itemlen - 8)
|
||||||
|
)
|
||||||
tiffStream.end = itemlen - 6
|
tiffStream.end = itemlen - 6
|
||||||
ignore = false
|
ignore = false
|
||||||
} else {
|
} else {
|
||||||
|
@ -428,11 +427,11 @@ private constructor(
|
||||||
|
|
||||||
private fun isIfdRequested(ifdType : Int) : Boolean {
|
private fun isIfdRequested(ifdType : Int) : Boolean {
|
||||||
when(ifdType) {
|
when(ifdType) {
|
||||||
IfdId.TYPE_IFD_0 -> return mOptions and ExifInterface.Options.OPTION_IFD_0 != 0
|
IfdData.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
|
IfdData.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
|
IfdData.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
|
IfdData.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_INTEROPERABILITY -> return mOptions and ExifInterface.Options.OPTION_IFD_INTEROPERABILITY != 0
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -440,17 +439,17 @@ private constructor(
|
||||||
private fun needToParseOffsetsInCurrentIfd() : Boolean {
|
private fun needToParseOffsetsInCurrentIfd() : Boolean {
|
||||||
return when(currentIfd) {
|
return when(currentIfd) {
|
||||||
|
|
||||||
IfdId.TYPE_IFD_0 ->
|
IfdData.TYPE_IFD_0 ->
|
||||||
isIfdRequested(IfdId.TYPE_IFD_EXIF) ||
|
isIfdRequested(IfdData.TYPE_IFD_EXIF) ||
|
||||||
isIfdRequested(IfdId.TYPE_IFD_GPS) ||
|
isIfdRequested(IfdData.TYPE_IFD_GPS) ||
|
||||||
isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY) ||
|
isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY) ||
|
||||||
isIfdRequested(IfdId.TYPE_IFD_1)
|
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
|
// The offset to interoperability IFD is located in Exif IFD
|
||||||
isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)
|
isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)
|
||||||
|
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -508,11 +507,11 @@ private constructor(
|
||||||
return EVENT_NEW_TAG
|
return EVENT_NEW_TAG
|
||||||
} else if(offset == endOfTags) {
|
} else if(offset == endOfTags) {
|
||||||
// There is a link to ifd1 at the end of ifd0
|
// 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()
|
val ifdOffset = readUnsignedLong()
|
||||||
if(isIfdRequested(IfdId.TYPE_IFD_1) || isThumbnailRequested) {
|
if(isIfdRequested(IfdData.TYPE_IFD_1) || isThumbnailRequested) {
|
||||||
if(ifdOffset != 0L) {
|
if(ifdOffset != 0L) {
|
||||||
registerIfd(IfdId.TYPE_IFD_1, ifdOffset)
|
registerIfd(IfdData.TYPE_IFD_1, ifdOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -610,9 +609,9 @@ private constructor(
|
||||||
}
|
}
|
||||||
val ifdOffset = readUnsignedLong()
|
val ifdOffset = readUnsignedLong()
|
||||||
// For ifd0, there is a link to ifd1 in the end of all tags
|
// 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) {
|
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 tid = tag.tagId
|
||||||
val ifd = tag.ifd
|
val ifd = tag.ifd
|
||||||
if(tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
|
if(tid == TAG_EXIF_IFD && checkAllowed(ifd, ExifInterface.TAG_EXIF_IFD)) {
|
||||||
if(isIfdRequested(IfdId.TYPE_IFD_EXIF) || isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
|
if(isIfdRequested(IfdData.TYPE_IFD_EXIF) || isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) {
|
||||||
registerIfd(IfdId.TYPE_IFD_EXIF, tag.getValueAt(0))
|
registerIfd(IfdData.TYPE_IFD_EXIF, tag.getValueAt(0))
|
||||||
}
|
}
|
||||||
} else if(tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
|
} else if(tid == TAG_GPS_IFD && checkAllowed(ifd, ExifInterface.TAG_GPS_IFD)) {
|
||||||
if(isIfdRequested(IfdId.TYPE_IFD_GPS)) {
|
if(isIfdRequested(IfdData.TYPE_IFD_GPS)) {
|
||||||
registerIfd(IfdId.TYPE_IFD_GPS, tag.getValueAt(0))
|
registerIfd(IfdData.TYPE_IFD_GPS, tag.getValueAt(0))
|
||||||
}
|
}
|
||||||
} else if(tid == TAG_INTEROPERABILITY_IFD && checkAllowed(
|
} else if(tid == TAG_INTEROPERABILITY_IFD && checkAllowed(
|
||||||
ifd,
|
ifd,
|
||||||
ExifInterface.TAG_INTEROPERABILITY_IFD
|
ExifInterface.TAG_INTEROPERABILITY_IFD
|
||||||
)) {
|
)) {
|
||||||
if(isIfdRequested(IfdId.TYPE_IFD_INTEROPERABILITY)) {
|
if(isIfdRequested(IfdData.TYPE_IFD_INTEROPERABILITY)) {
|
||||||
registerIfd(IfdId.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0))
|
registerIfd(IfdData.TYPE_IFD_INTEROPERABILITY, tag.getValueAt(0))
|
||||||
}
|
}
|
||||||
} else if(tid == TAG_JPEG_INTERCHANGE_FORMAT && checkAllowed(
|
} else if(tid == TAG_JPEG_INTERCHANGE_FORMAT && checkAllowed(
|
||||||
ifd,
|
ifd,
|
||||||
|
@ -750,7 +749,7 @@ private constructor(
|
||||||
}
|
}
|
||||||
} else if(tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
|
} else if(tid == TAG_STRIP_OFFSETS && checkAllowed(ifd, ExifInterface.TAG_STRIP_OFFSETS)) {
|
||||||
if(isThumbnailRequested) {
|
if(isThumbnailRequested) {
|
||||||
if(tag.hasValue()) {
|
if(tag.hasValue) {
|
||||||
for(i in 0 until tag.componentCount) {
|
for(i in 0 until tag.componentCount) {
|
||||||
if(tag.dataType == ExifTag.TYPE_UNSIGNED_SHORT) {
|
if(tag.dataType == ExifTag.TYPE_UNSIGNED_SHORT) {
|
||||||
registerUncompressedStrip(i, tag.getValueAt(i))
|
registerUncompressedStrip(i, tag.getValueAt(i))
|
||||||
|
@ -765,16 +764,16 @@ private constructor(
|
||||||
} else if(tid == TAG_STRIP_BYTE_COUNTS && checkAllowed(
|
} else if(tid == TAG_STRIP_BYTE_COUNTS && checkAllowed(
|
||||||
ifd,
|
ifd,
|
||||||
ExifInterface.TAG_STRIP_BYTE_COUNTS
|
ExifInterface.TAG_STRIP_BYTE_COUNTS
|
||||||
) && isThumbnailRequested && tag.hasValue()) {
|
) && isThumbnailRequested && tag.hasValue) {
|
||||||
mStripSizeTag = tag
|
mStripSizeTag = tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isDefinedTag(ifdId : Int, tagId : Int) : Boolean {
|
fun isDefinedTag(ifdId : Int, tagId : Short) : Boolean {
|
||||||
return mInterface.tagInfo.get(
|
return mInterface.tagInfo.get(
|
||||||
ExifInterface.defineTag(
|
ExifInterface.defineTag(
|
||||||
ifdId,
|
ifdId,
|
||||||
tagId.toShort()
|
tagId
|
||||||
)
|
)
|
||||||
) != ExifInterface.DEFINITION_NULL
|
) != ExifInterface.DEFINITION_NULL
|
||||||
}
|
}
|
||||||
|
@ -946,12 +945,8 @@ private constructor(
|
||||||
internal var isRequested : Boolean
|
internal var isRequested : Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
class Section {
|
class Section(var size : Int, var type : Int, var data : ByteArray)
|
||||||
internal var size : Int = 0
|
|
||||||
internal var type : Int = 0
|
|
||||||
internal var data : ByteArray? = null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "ExifParser"
|
private const val TAG = "ExifParser"
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,13 @@ internal class ExifReader(private val mInterface : ExifInterface) {
|
||||||
@Throws(ExifInvalidFormatException::class, IOException::class)
|
@Throws(ExifInvalidFormatException::class, IOException::class)
|
||||||
fun read(inputStream : InputStream, options : Int) : ExifData {
|
fun read(inputStream : InputStream, options : Int) : ExifData {
|
||||||
val parser = ExifParser.parse(inputStream, options, mInterface)
|
val parser = ExifParser.parse(inputStream, options, mInterface)
|
||||||
val exifData = ExifData(parser.byteOrder )
|
val exifData = ExifData(
|
||||||
exifData.sections = parser.sections
|
byteOrder = parser.byteOrder,
|
||||||
exifData.mUncompressedDataPosition = parser.uncompressedDataPosition
|
sections = parser.sections,
|
||||||
|
mUncompressedDataPosition = parser.uncompressedDataPosition,
|
||||||
exifData.qualityGuess = parser.qualityGuess
|
qualityGuess = parser.qualityGuess,
|
||||||
exifData.jpegProcess = parser.jpegProcess
|
jpegProcess = parser.jpegProcess
|
||||||
|
)
|
||||||
|
|
||||||
val w = parser.imageWidth
|
val w = parser.imageWidth
|
||||||
val h = parser.imageLength
|
val h = parser.imageLength
|
||||||
|
@ -51,33 +52,34 @@ internal class ExifReader(private val mInterface : ExifInterface) {
|
||||||
exifData.setImageSize(w, h)
|
exifData.setImageSize(w, h)
|
||||||
}
|
}
|
||||||
|
|
||||||
var tag : ExifTag?
|
|
||||||
|
|
||||||
var event = parser.next()
|
var event = parser.next()
|
||||||
while(event != ExifParser.EVENT_END) {
|
while(event != ExifParser.EVENT_END) {
|
||||||
when(event) {
|
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 -> {
|
ExifParser.EVENT_NEW_TAG -> {
|
||||||
tag = parser.tag
|
val tag = parser.tag
|
||||||
|
when {
|
||||||
|
tag == null ->
|
||||||
|
Log.w(TAG, "parser.tag is null")
|
||||||
if(! tag !!.hasValue()) {
|
|
||||||
parser.registerForTagValue(tag)
|
! tag.hasValue ->
|
||||||
} else {
|
parser.registerForTagValue(tag)
|
||||||
// Log.v(TAG, "parsing id " + tag.getTagId() + " = " + tag);
|
|
||||||
if(parser.isDefinedTag(tag.ifd, tag.tagId.toInt())) {
|
! parser.isDefinedTag(tag.ifd, tag.tagId) ->
|
||||||
exifData.getIfdData(tag.ifd) !!.setTag(tag)
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "skip tag because not registered in the tag table:$tag")
|
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 -> {
|
ExifParser.EVENT_VALUE_OF_REGISTERED_TAG -> {
|
||||||
tag = parser.tag
|
val tag = parser.tag !!
|
||||||
if(tag !!.dataType == ExifTag.TYPE_UNDEFINED) {
|
if(tag.dataType == ExifTag.TYPE_UNDEFINED) {
|
||||||
parser.readFullTagValue(tag)
|
parser.readFullTagValue(tag)
|
||||||
}
|
}
|
||||||
exifData.getIfdData(tag.ifd) !!.setTag(tag)
|
exifData.getIfdData(tag.ifd) !!.setTag(tag)
|
||||||
|
|
|
@ -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.
|
// The ifd that this tag should be put in. the ID of the IFD this tag belongs to.
|
||||||
/*
|
/*
|
||||||
* @see IfdId.TYPE_IFD_0
|
* @see IfdData.TYPE_IFD_0
|
||||||
* @see IfdId.TYPE_IFD_1
|
* @see IfdData.TYPE_IFD_1
|
||||||
* @see IfdId.TYPE_IFD_EXIF
|
* @see IfdData.TYPE_IFD_EXIF
|
||||||
* @see IfdId.TYPE_IFD_GPS
|
* @see IfdData.TYPE_IFD_GPS
|
||||||
* @see IfdId.TYPE_IFD_INTEROPERABILITY
|
* @see IfdData.TYPE_IFD_INTEROPERABILITY
|
||||||
*/
|
*/
|
||||||
var ifd : Int,
|
var ifd : Int,
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ open class ExifTag internal constructor(
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// TODO: fix integer overflows with this
|
// TODO: fix integer overflows with this
|
||||||
var componentCount : Int = 0
|
var componentCount : Int = componentCount
|
||||||
private set
|
private set
|
||||||
|
|
||||||
// The value (array of elements of type Tag Type)
|
// The value (array of elements of type Tag Type)
|
||||||
|
@ -83,7 +83,6 @@ open class ExifTag internal constructor(
|
||||||
val dataSize : Int
|
val dataSize : Int
|
||||||
get() = componentCount * getElementSize(dataType)
|
get() = componentCount * getElementSize(dataType)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value as a byte array. This method should be used for tags of
|
* Gets the value as a byte array. This method should be used for tags of
|
||||||
* type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
|
* type [.TYPE_UNDEFINED] or [.TYPE_UNSIGNED_BYTE].
|
||||||
|
@ -124,9 +123,9 @@ open class ExifTag internal constructor(
|
||||||
*/
|
*/
|
||||||
// Truncates
|
// Truncates
|
||||||
val valueAsInts : IntArray?
|
val valueAsInts : IntArray?
|
||||||
get() = when(val v = mValue){
|
get() = when(val v = mValue) {
|
||||||
is LongArray-> IntArray(v.size){ v[it].toInt()}
|
is LongArray -> IntArray(v.size) { v[it].toInt() }
|
||||||
else ->null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,7 +142,6 @@ open class ExifTag internal constructor(
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the [.TYPE_ASCII] data.
|
* Gets the [.TYPE_ASCII] data.
|
||||||
*
|
*
|
||||||
|
@ -159,11 +157,6 @@ open class ExifTag internal constructor(
|
||||||
val stringByte : ByteArray?
|
val stringByte : ByteArray?
|
||||||
get() = mValue as? ByteArray
|
get() = mValue as? ByteArray
|
||||||
|
|
||||||
init {
|
|
||||||
this.componentCount = componentCount
|
|
||||||
mValue = null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the component count of this tag. Call this function before
|
* Sets the component count of this tag. Call this function before
|
||||||
* setValue() if the length of value does not match the component count.
|
* 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
|
* Returns true if this ExifTag contains value; otherwise, this tag will
|
||||||
* contain an offset value that is determined when the tag is written.
|
* contain an offset value that is determined when the tag is written.
|
||||||
*/
|
*/
|
||||||
fun hasValue() : Boolean {
|
val hasValue :Boolean
|
||||||
return mValue != null
|
get() = mValue != null
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets integer values into this tag. This method should be used for tags of
|
* 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.
|
* * The component count in the definition of this tag is not 1.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun setValue(value : Int) : Boolean {
|
fun setValue(value : Int) = setValue(intArrayOf(value))
|
||||||
return setValue(intArrayOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets long values into this tag. This method should be used for tags of
|
* 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.
|
* * The component count in the definition for this tag is not 1.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun setValue(value : Long) : Boolean {
|
fun setValue(value : Long) = setValue(longArrayOf(value))
|
||||||
return setValue(longArrayOf(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets Rational values into this tag. This method should be used for tags
|
* Sets Rational values into this tag. This method should be used for tags
|
||||||
|
@ -309,8 +297,7 @@ open class ExifTag internal constructor(
|
||||||
*
|
*
|
||||||
* @see Rational
|
* @see Rational
|
||||||
*/
|
*/
|
||||||
fun setValue(value : Rational) : Boolean =
|
fun setValue(value : Rational) =setValue(arrayOf(value))
|
||||||
setValue(arrayOf(value))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets byte values into this tag. This method should be used for tags of
|
* 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
|
return false
|
||||||
}
|
}
|
||||||
componentCount = length
|
componentCount = length
|
||||||
mValue = ByteArray(length).also{
|
mValue = ByteArray(length).also {
|
||||||
System.arraycopy(value, offset, it, 0, length)
|
System.arraycopy(value, offset, it, 0, length)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -348,8 +335,7 @@ open class ExifTag internal constructor(
|
||||||
* * The component count in the definition for this tag is not 1.
|
* * The component count in the definition for this tag is not 1.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
fun setValue(value : Byte) : Boolean =
|
fun setValue(value : Byte) = setValue(byteArrayOf(value))
|
||||||
setValue(byteArrayOf(value))
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the value for this tag using an appropriate setValue method for the
|
* 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 Int -> return setValue(obj.toInt())
|
||||||
is Long -> return setValue(obj.toLong())
|
is Long -> return setValue(obj.toLong())
|
||||||
|
|
||||||
else ->{
|
else -> {
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val ra = obj as? Array<Rational>
|
val ra = obj as? Array<Rational>
|
||||||
if(ra != null) return setValue( ra )
|
if(ra != null) return setValue(ra)
|
||||||
|
|
||||||
// Nulls in this array are treated as zeroes.
|
// Nulls in this array are treated as zeroes.
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val sa = obj as? Array<Short?>
|
val sa = obj as? Array<Short?>
|
||||||
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.
|
// Nulls in this array are treated as zeroes.
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val ia = obj as? Array<Int?>
|
val ia = obj as? Array<Int?>
|
||||||
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.
|
// Nulls in this array are treated as zeroes.
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val la = obj as? Array<Long?>
|
val la = obj as? Array<Long?>
|
||||||
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.
|
// Nulls in this array are treated as zeroes.
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val ba = obj as? Array<Byte?>
|
val ba = obj as? Array<Byte?>
|
||||||
if( ba != null) return setValue(ByteArray(ba.size){ ba[it] ?: 0 })
|
if(ba != null) return setValue(ByteArray(ba.size) { ba[it] ?: 0 })
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -487,7 +475,7 @@ open class ExifTag internal constructor(
|
||||||
*/
|
*/
|
||||||
fun getValueAsByte(defaultValue : Byte) : Byte {
|
fun getValueAsByte(defaultValue : Byte) : Byte {
|
||||||
val array = valueAsBytes
|
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.
|
* @return the tag's value as a Rational, or the defaultValue.
|
||||||
*/
|
*/
|
||||||
fun getValueAsRational(defaultValue : Long) : Rational =
|
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
|
* 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 {
|
private fun getValueAsRational(defaultValue : Rational) : Rational {
|
||||||
val array = valueAsRationals
|
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 {
|
fun getValueAsInt(defaultValue : Int) : Int {
|
||||||
val array = valueAsInts
|
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 {
|
fun getValueAsLong(defaultValue : Long) : Long {
|
||||||
val array = valueAsLongs
|
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
|
var hasDefinedCount : Boolean
|
||||||
get() = mHasDefinedDefaultComponentCount
|
get() = mHasDefinedDefaultComponentCount
|
||||||
set(value){
|
set(value) {
|
||||||
mHasDefinedDefaultComponentCount = value
|
mHasDefinedDefaultComponentCount = value
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean {
|
private fun checkOverflowForUnsignedShort(value : IntArray) : Boolean =
|
||||||
for(v in value) {
|
null != value.find { it !in 0 .. UNSIGNED_SHORT_MAX }
|
||||||
if(v > UNSIGNED_SHORT_MAX || v < 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean {
|
private fun checkOverflowForUnsignedLong(value : LongArray) : Boolean =
|
||||||
for(v in value) {
|
null != value.find { it !in 0 .. UNSIGNED_LONG_MAX }
|
||||||
if(v < 0 || v > UNSIGNED_LONG_MAX) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean {
|
private fun checkOverflowForUnsignedLong(value : IntArray) : Boolean =
|
||||||
for(v in value) {
|
null != value.find { it < 0 }
|
||||||
if(v < 0) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkOverflowForUnsignedRational(value : Array<Rational>) : Boolean {
|
private fun checkOverflowForUnsignedRational(value : Array<Rational>) : Boolean =
|
||||||
for(v in value) {
|
null != value.find { it.numerator !in 0 .. UNSIGNED_LONG_MAX || it.denominator !in 0 .. UNSIGNED_LONG_MAX }
|
||||||
if(v.numerator < 0 || v.denominator < 0 || v.numerator > UNSIGNED_LONG_MAX || v.denominator > UNSIGNED_LONG_MAX) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkOverflowForRational(value : Array<Rational>) : Boolean {
|
private fun checkOverflowForRational(value : Array<Rational>) : Boolean =
|
||||||
for(v in value) {
|
null != value.find { it.numerator !in LONG_MIN .. LONG_MAX || it.denominator !in LONG_MIN .. LONG_MAX }
|
||||||
if(v.numerator < LONG_MIN || v.denominator < LONG_MIN || v.numerator > LONG_MAX || v.denominator > LONG_MAX) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode() : Int {
|
override fun hashCode() : Int {
|
||||||
var result = tagId.toInt()
|
var result = tagId.toInt()
|
||||||
|
@ -766,8 +724,6 @@ open class ExifTag internal constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* The BYTE type in the EXIF standard. An 8-bit unsigned integer.
|
* 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 =
|
fun isValidIfd(ifdId : Int) : Boolean =
|
||||||
when(ifdId) {
|
when(ifdId) {
|
||||||
IfdId.TYPE_IFD_0,
|
IfdData.TYPE_IFD_0,
|
||||||
IfdId.TYPE_IFD_1,
|
IfdData.TYPE_IFD_1,
|
||||||
IfdId.TYPE_IFD_EXIF,
|
IfdData.TYPE_IFD_EXIF,
|
||||||
IfdId.TYPE_IFD_INTEROPERABILITY,
|
IfdData.TYPE_IFD_INTEROPERABILITY,
|
||||||
IfdId.TYPE_IFD_GPS -> true
|
IfdData.TYPE_IFD_GPS -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -883,11 +839,4 @@ open class ExifTag internal constructor(
|
||||||
else -> ""
|
else -> ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Equivalent to setValue(value, 0, value.length).
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Equivalent to getBytes(buffer, 0, buffer.length).
|
|
||||||
*/
|
|
||||||
|
|
|
@ -18,35 +18,33 @@ package it.sephiroth.android.library.exif2
|
||||||
|
|
||||||
import java.util.HashMap
|
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.
|
// This class stores all the tags in an IFD.
|
||||||
// an IfdData with given IFD ID.
|
// an IfdData with given IFD ID.
|
||||||
internal class IfdData(
|
internal class IfdData(
|
||||||
// the ID of this IFD.
|
val id : Int // the ID of this IFD.
|
||||||
val id : Int
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
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<Short, ExifTag>()
|
private val mExifTags = HashMap<Short, ExifTag>()
|
||||||
|
|
||||||
// the offset of next IFD.
|
// the offset of next IFD.
|
||||||
|
@ -56,9 +54,9 @@ internal class IfdData(
|
||||||
val tagCount : Int
|
val tagCount : Int
|
||||||
get() = mExifTags.size
|
get() = mExifTags.size
|
||||||
|
|
||||||
// a array the contains all [ExifTag] in this IFD.
|
// Collection the contains all [ExifTag] in this IFD.
|
||||||
val allTags : Array<ExifTag>
|
val allTagsCollection : Collection<ExifTag>
|
||||||
get() = mExifTags.values.toTypedArray()
|
get() = mExifTags.values
|
||||||
|
|
||||||
// checkCollision
|
// checkCollision
|
||||||
fun contains(tagId : Short) : Boolean {
|
fun contains(tagId : Short) : Boolean {
|
||||||
|
@ -87,19 +85,12 @@ internal class IfdData(
|
||||||
* IFDs offset or thumbnail offset will be ignored.
|
* IFDs offset or thumbnail offset will be ignored.
|
||||||
*/
|
*/
|
||||||
override fun equals(other : Any?) : Boolean {
|
override fun equals(other : Any?) : Boolean {
|
||||||
if(other === null) return false
|
|
||||||
if(other === this) return true
|
|
||||||
if(other is IfdData) {
|
if(other is IfdData) {
|
||||||
|
if(other === this) return true
|
||||||
if(other.id == id && other.tagCount == tagCount) {
|
if(other.id == id && other.tagCount == tagCount) {
|
||||||
val tags = other.allTags
|
for(tag in other.allTagsCollection) {
|
||||||
for(tag in tags) {
|
if(ExifInterface.isOffsetTag(tag.tagId)) continue
|
||||||
if(ExifInterface.isOffsetTag(tag.tagId)) {
|
if(tag != mExifTags[tag.tagId]) return false
|
||||||
continue
|
|
||||||
}
|
|
||||||
val tag2 = mExifTags[tag.tagId]
|
|
||||||
if(tag != tag2) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ class Rational(
|
||||||
) {
|
) {
|
||||||
|
|
||||||
// copy from a Rational.
|
// copy from a Rational.
|
||||||
|
@Suppress("unused")
|
||||||
constructor(r : Rational) : this(
|
constructor(r : Rational) : this(
|
||||||
numerator = r.numerator,
|
numerator = r.numerator,
|
||||||
denominator = r.denominator
|
denominator = r.denominator
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package it.sephiroth.android.library.exif2
|
package it.sephiroth.android.library.exif2.utils
|
||||||
|
|
||||||
import java.io.EOFException
|
import java.io.EOFException
|
||||||
import java.io.FilterInputStream
|
import java.io.FilterInputStream
|
|
@ -14,8 +14,9 @@
|
||||||
* limitations under the License.
|
* 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.FilterOutputStream
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
|
@ -0,0 +1,7 @@
|
||||||
|
package it.sephiroth.android.library.exif2.utils
|
||||||
|
|
||||||
|
internal fun <E> Collection<E>?.notEmpty() : Collection<E>? =
|
||||||
|
if(this?.isNotEmpty() == true) this else null
|
||||||
|
|
||||||
|
internal fun <E> List<E>?.notEmpty() : List<E>? =
|
||||||
|
if(this?.isNotEmpty() == true) this else null
|
|
@ -2,8 +2,7 @@ package it.sephiroth.android.library.exif2
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.util.SparseIntArray
|
import android.util.SparseIntArray
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.*
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
@ -12,7 +11,7 @@ class Test1 {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLog() {
|
fun testLog() {
|
||||||
Log.v("TEST","test")
|
Log.v("TEST", "test")
|
||||||
assertTrue("using android.util.Log", true)
|
assertTrue("using android.util.Log", true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,29 +31,48 @@ class Test1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOrientation(fileName : String) : Pair<Int?,Throwable?> =
|
private fun getOrientation(fileName : String) : Pair<Int?, Throwable?> =
|
||||||
try{
|
try {
|
||||||
val o = FileInputStream(getFile(fileName)).use { inStream ->
|
val o = FileInputStream(getFile(fileName)).use { inStream ->
|
||||||
ExifInterface()
|
ExifInterface()
|
||||||
.apply {
|
.apply {
|
||||||
readExif(
|
readExif(
|
||||||
inStream,
|
inStream,
|
||||||
ExifInterface.Options.OPTION_IFD_0
|
ExifInterface.Options.OPTION_IFD_0
|
||||||
or ExifInterface.Options.OPTION_IFD_1
|
or ExifInterface.Options.OPTION_IFD_1
|
||||||
or ExifInterface.Options.OPTION_IFD_EXIF
|
or ExifInterface.Options.OPTION_IFD_EXIF
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
.getTagIntValue(ExifInterface.TAG_ORIENTATION)
|
||||||
|
}
|
||||||
|
Pair(o, null)
|
||||||
|
} catch(ex : Throwable) {
|
||||||
|
Pair(null, ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getThumbnailBytes(fileName : String) : Pair<ByteArray?, Throwable?> =
|
||||||
|
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) {
|
private fun testNotJpegSub(fileName : String) {
|
||||||
val(o,ex) = getOrientation(fileName)
|
val (o, ex) = getOrientation(fileName)
|
||||||
assertTrue("testNotJpegSub",o ==null && ex!=null)
|
assertTrue("testNotJpegSub", o == null && ex != null)
|
||||||
if( ex!= null) println("exception raised: ${ex::class.java} ${ex.message}")
|
if(ex != null) println("exception raised: ${ex::class.java} ${ex.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -66,23 +84,32 @@ class Test1 {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testJpeg() {
|
fun testJpeg() {
|
||||||
var fileName :String
|
var fileName : String
|
||||||
var rv : Pair<Int?,Throwable?>
|
var rvO : Pair<Int?, Throwable?>
|
||||||
|
var rvT : Pair<ByteArray?, Throwable?>
|
||||||
// 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)
|
|
||||||
|
|
||||||
// this file has orientation 6.
|
// this file has orientation 6.
|
||||||
fileName = "test3.jpg"
|
fileName = "test3.jpg"
|
||||||
rv = getOrientation(fileName)
|
rvO = getOrientation(fileName)
|
||||||
assertEquals(fileName,rv.first , 6)
|
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) // <java.lang.IllegalStateException: stop before hitting compressed data>
|
||||||
|
|
||||||
|
rvT = getThumbnailBytes(fileName)
|
||||||
|
assertNotNull(fileName,rvT.second) // <java.lang.IllegalStateException: stop before hitting compressed data>
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue